From d1aa0db53708d0424c01affbdaf49439c9185d10 Mon Sep 17 00:00:00 2001 From: isaac <> Date: Sat, 25 Apr 2026 20:48:15 +0400 Subject: [PATCH] Postbox -> TelegramEngine waves 46-93 (squashed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 `.account.postbox.mediaBox.X(...)` to `.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) --- CLAUDE.md | 2 +- ...eerhistory-openclearhistory-engine-peer.md | 116 ++++ ...4-25-peerinfo-enclosingpeer-engine-peer.md | 516 ++++++++++++++ ...infoscreendata-linked-peers-engine-peer.md | 106 +++ ...creendata-savedmessagespeer-engine-peer.md | 70 ++ ...04-25-phn-peer-stored-field-engine-peer.md | 62 ++ docs/superpowers/postbox-refactor-log.md | 655 ++++++++++++++++++ ...4-25-peerinfo-enclosingpeer-engine-peer.md | 127 ++++ .../Sources/AccountContext.swift | 12 +- .../Sources/AnimatedAvatarSetNode.swift | 4 +- .../Sources/AttachmentController.swift | 4 +- .../Sources/AttachmentPanel.swift | 4 +- .../AvatarNode/Sources/AvatarNode.swift | 6 +- .../AvatarNode/Sources/PeerAvatar.swift | 4 +- .../Sources/AvatarVideoNode.swift | 4 +- .../BrowserUI/Sources/BrowserMarkdown.swift | 4 +- .../Sources/ChatListController.swift | 8 +- .../Sources/ChatListSearchContainerNode.swift | 2 +- .../Sources/ChatListSearchListPaneNode.swift | 10 +- .../Sources/ReactionImageComponent.swift | 2 +- .../Sources/ContactContextMenus.swift | 2 +- .../Items/ChatDocumentGalleryItem.swift | 4 +- .../Items/ChatExternalFileGalleryItem.swift | 2 +- .../Sources/Items/ChatImageGalleryItem.swift | 4 +- .../Sources/InAppPurchaseManager.swift | 5 +- .../Sources/InstantImageGalleryItem.swift | 4 +- .../InstantPagePlayableVideoNode.swift | 2 +- .../Sources/SecureIdAuthController.swift | 4 +- .../Sources/SecureIdAuthControllerNode.swift | 6 +- .../Sources/SecureIdAuthControllerState.swift | 8 +- .../Sources/SecureIdAuthHeaderNode.swift | 4 +- .../Sources/AvatarGalleryController.swift | 12 +- .../AvatarGalleryItemFooterContentNode.swift | 2 +- .../Sources/PeerAvatarImageGalleryItem.swift | 8 +- .../Sources/PeerInfoAvatarListNode.swift | 2 +- .../Sources/ChannelVisibilityController.swift | 2 +- .../Sources/ReactionSelectionNode.swift | 9 +- .../NotificationsAndSoundsController.swift | 14 +- .../BlockedPeersController.swift | 16 +- .../IncomingMessagePrivacyScreen.swift | 2 +- .../Recent Sessions/ItemListWebsiteItem.swift | 11 +- .../RecentSessionsController.swift | 12 +- .../SelectivePrivacySettingsController.swift | 2 +- ...ectivePrivacySettingsPeersController.swift | 2 +- .../Search/SettingsSearchableItems.swift | 14 +- .../Sources/ThemePickerController.swift | 3 +- .../Sources/ThemePickerGridItem.swift | 2 +- .../Themes/ThemeSettingsController.swift | 5 +- .../Sources/ShareController.swift | 14 +- .../Sources/SharePeersContainerNode.swift | 2 +- .../Sources/ShareSearchContainerNode.swift | 2 +- .../Sources/ChannelStatsController.swift | 10 +- .../Sources/MessageStatsController.swift | 6 +- .../Sources/StatsMessageItem.swift | 8 +- .../Sources/CallControllerNodeV2.swift | 2 +- .../VideoChatParticipantVideoComponent.swift | 2 +- ...ideoChatScreenParticipantContextMenu.swift | 4 +- .../Sources/VoiceChatJoinScreen.swift | 10 +- .../Sources/ApiUtils/ApiUtils.swift | 4 + .../Sources/Settings/PrivacySettings.swift | 4 + .../ChangePeerNotificationSettings.swift | 4 +- .../Peers/InactiveChannels.swift | 10 +- .../Peers/NotificationExceptionsList.swift | 12 +- .../Sources/TelegramEngine/Peers/Peer.swift | 20 + .../Peers/TelegramEnginePeers.swift | 7 +- .../Sources/Utils/CanSendMessagesToPeer.swift | 19 +- .../Sources/Utils/PeerUtils.swift | 4 + .../Utils/StoredMessageFromSearchPeer.swift | 12 +- .../Sources/ChatAdPanelNode.swift | 2 +- .../Sources/AttachmentFileController.swift | 4 +- .../ChatMessageAttachedContentNode.swift | 2 +- ...atMessageInteractiveInstantVideoNode.swift | 2 +- .../ChatMessageInteractiveMediaNode.swift | 4 +- ...essageJoinedChannelBubbleContentNode.swift | 2 +- .../ChatMessagePollBubbleContentNode.swift | 8 +- .../Sources/ChatQrCodeScreen.swift | 16 +- .../Sources/ChatRecentActionsController.swift | 12 +- .../Sources/ChatUserInfoItem.swift | 4 +- .../Sources/MergedAvatarsNode.swift | 4 +- .../Sources/ChatListNoticeItem.swift | 2 +- .../Sources/ChatThemeScreen.swift | 3 +- .../Sources/AgeVerification.swift | 2 +- .../Sources/AgeVerificationScreen.swift | 2 +- .../Sources/ImageObjectSeparation.swift | 2 +- .../Sources/EditStories.swift | 2 +- .../Sources/PeerInfoCoverComponent.swift | 4 +- .../ListItems/PeerInfoScreenMemberItem.swift | 16 +- .../PeerInfoGroupsInCommonPaneNode.swift | 22 +- .../Sources/Panes/PeerInfoMembersPane.swift | 26 +- .../Panes/PeerInfoRecommendedPeersPane.swift | 10 +- .../Sources/PeerInfoAvatarListNode.swift | 4 +- ...PeerInfoAvatarTransformContainerNode.swift | 14 +- .../PeerInfoScreen/Sources/PeerInfoData.swift | 48 +- .../Sources/PeerInfoEditingAvatarNode.swift | 4 +- .../PeerInfoHeaderEditingContentNode.swift | 6 +- .../Sources/PeerInfoHeaderNode.swift | 10 +- .../Sources/PeerInfoInteraction.swift | 4 +- .../Sources/PeerInfoMembers.swift | 8 +- .../Sources/PeerInfoPaneContainerNode.swift | 4 +- .../Sources/PeerInfoProfileItems.swift | 10 +- .../Sources/PeerInfoScreen.swift | 48 +- .../Sources/PeerInfoScreenOpenChat.swift | 4 +- .../Sources/PeerInfoScreenOpenMember.swift | 2 +- .../Sources/PeerInfoScreenOpenURL.swift | 9 +- .../PeerInfoScreenPerformButtonAction.swift | 20 +- .../Sources/PeerInfoSettingsItems.swift | 4 +- .../Sources/PresentAddMembers.swift | 8 +- .../Sources/PeerInfoStoryGridScreen.swift | 2 +- .../Sources/PeerInfoStoryPaneNode.swift | 6 +- .../Sources/OldChannelsController.swift | 6 +- .../Sources/OldChannelsSearch.swift | 2 +- .../Sources/PeerSelectionController.swift | 2 +- .../Sources/GiftAvatarComponent.swift | 2 +- .../Sources/SettingsThemeWallpaperNode.swift | 2 +- .../Sources/ThemeCarouselItem.swift | 2 +- .../Sources/WallpaperGalleryItem.swift | 6 +- .../Sources/ShareExtensionContext.swift | 10 +- .../Sources/StarsTransactionScreen.swift | 4 +- .../Sources/StarsTransactionsScreen.swift | 2 +- .../Sources/PeerListItemComponent.swift | 2 +- .../Sources/LiveChatReactionStreamView.swift | 4 +- .../Sources/StoryChatContent.swift | 28 +- .../Sources/StoryItemContentComponent.swift | 4 +- .../Sources/StoryItemImageView.swift | 2 +- .../Sources/StoryItemOverlaysView.swift | 6 +- .../StoryItemSetContainerComponent.swift | 14 +- .../Sources/StorySetIndicatorComponent.swift | 2 +- .../Sources/TextProcessingScreen.swift | 2 +- .../Chat/ChatControllerLoadDisplayNode.swift | 20 +- .../TelegramUI/Sources/ChatController.swift | 2 +- .../ChatPinnedMessageTitlePanelNode.swift | 2 +- .../MentionChatInputContextPanelNode.swift | 2 +- .../Sources/MentionChatInputPanelItem.swift | 16 +- .../TelegramUI/Sources/OpenResolvedUrl.swift | 2 +- .../OverlayAudioPlayerControllerNode.swift | 2 +- .../OverlayAudioPlayerControlsNode.swift | 3 +- .../Sources/SharedAccountContext.swift | 17 +- .../Sources/UndoOverlayControllerNode.swift | 4 +- .../WebUI/Sources/WebAppController.swift | 2 +- 139 files changed, 2124 insertions(+), 453 deletions(-) create mode 100644 docs/superpowers/plans/2026-04-25-clearpeerhistory-openclearhistory-engine-peer.md create mode 100644 docs/superpowers/plans/2026-04-25-peerinfo-enclosingpeer-engine-peer.md create mode 100644 docs/superpowers/plans/2026-04-25-peerinfoscreendata-linked-peers-engine-peer.md create mode 100644 docs/superpowers/plans/2026-04-25-peerinfoscreendata-savedmessagespeer-engine-peer.md create mode 100644 docs/superpowers/plans/2026-04-25-phn-peer-stored-field-engine-peer.md create mode 100644 docs/superpowers/specs/2026-04-25-peerinfo-enclosingpeer-engine-peer.md diff --git a/CLAUDE.md b/CLAUDE.md index b480e49e44..8a6239dbda 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ A gradual migration is underway to eliminate direct `import Postbox` from consum **Historical record:** Wave-by-wave outcomes, the running tally of Postbox-free modules, and full verbose forms of the guidance subsections below live in [`docs/superpowers/postbox-refactor-log.md`](docs/superpowers/postbox-refactor-log.md). Read that file when you need wave-specific context, a full worked example of a pattern, or the history of a particular module's migration. -Waves landed so far (as of 2026-04-24): 44 waves plus standalone cleanups. See the log file for per-wave detail; the list of still-open migration opportunities lives in the `project_postbox_refactor_next_wave.md` memory file. +Waves landed so far (as of 2026-04-24): 45 waves plus standalone cleanups. See the log file for per-wave detail; the list of still-open migration opportunities lives in the `project_postbox_refactor_next_wave.md` memory file. ### Rules that apply to every wave diff --git a/docs/superpowers/plans/2026-04-25-clearpeerhistory-openclearhistory-engine-peer.md b/docs/superpowers/plans/2026-04-25-clearpeerhistory-openclearhistory-engine-peer.md new file mode 100644 index 0000000000..1b847bd2d5 --- /dev/null +++ b/docs/superpowers/plans/2026-04-25-clearpeerhistory-openclearhistory-engine-peer.md @@ -0,0 +1,116 @@ +# Wave 54: ClearPeerHistory.init + openClearHistory `chatPeer: Peer → EnginePeer` + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Migrate the `chatPeer:` parameter type on both `ClearPeerHistory.init` and `openClearHistory` from `Peer` to `EnginePeer`. Closes wave-53's deferred sibling. + +**Wave shape:** Bundled method-signature migration (familiar from waves 41/44/47/50/53). Mechanical `as?`/`is` cluster on a single field, with EnginePeer.init boundary lifts at each call site. + +**Tech Stack:** Swift, Bazel, Make.py. + +--- + +## Pre-Flight Inventory (validated 2026-04-25) + +**2 files modified, 16 edits.** + +### File 1: `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift` (PIS) + +| Line | Current | After | Note | +|---|---|---|---| +| 3213 | `func openClearHistory(... peer: Peer, chatPeer: Peer) {` | `... chatPeer: EnginePeer)` | type-site | +| 3230 | `EnginePeer(chatPeer).compactDisplayTitle` | `chatPeer.compactDisplayTitle` | drop wrap | +| 3232 | `EnginePeer(chatPeer).compactDisplayTitle` | `chatPeer.compactDisplayTitle` | drop wrap | +| 3251 | `EnginePeer(chatPeer).compactDisplayTitle` | `chatPeer.compactDisplayTitle` | drop wrap | +| 3269 | `EnginePeer(chatPeer).compactDisplayTitle` | `chatPeer.compactDisplayTitle` | drop wrap | +| 7416 | `init(... peer: Peer, chatPeer: Peer, cachedData: ...)` | `... chatPeer: EnginePeer, cachedData: ...` | type-site | +| 7421 | `} else if chatPeer is TelegramSecretChat {` | `} else if case .secretChat = chatPeer {` | conversion | +| 7425 | `} else if let group = chatPeer as? TelegramGroup {` | `} else if case let .legacyGroup(group) = chatPeer {` | conversion | +| 7436 | `} else if let channel = chatPeer as? TelegramChannel {` | `} else if case let .channel(channel) = chatPeer {` | conversion | +| 7464 | `if let user = chatPeer as? TelegramUser, user.botInfo != nil {` | `if case let .user(user) = chatPeer, user.botInfo != nil {` | conversion | + +`peer:` parameter stays Peer-typed in both functions: `openClearHistory` doesn't reference `peer` in its body; `ClearPeerHistory.init` uses only `peer.id == context.account.peerId` (line 7417), which works on Peer (and would also work on EnginePeer, but migrating it would require 6 boundary lifts at PISPBA call sites for no internal benefit). + +### File 2: `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift` (PISPBA) + +| Line | Current | After | Note | +|---|---|---|---| +| 851 | `chatPeer: chatPeer._asPeer()` | `chatPeer: chatPeer` | drop wave-53 ADD | +| 857 | `chatPeer: user` | `chatPeer: EnginePeer(user)` | boundary lift (TelegramUser) | +| 1067 | `chatPeer: channel` | `chatPeer: EnginePeer(channel)` | boundary lift (TelegramChannel) | +| 1073 | `chatPeer: channel` | `chatPeer: EnginePeer(channel)` | boundary lift (TelegramChannel) | +| 1234 | `chatPeer: group` | `chatPeer: EnginePeer(group)` | boundary lift (TelegramGroup) | +| 1240 | `chatPeer: group` | `chatPeer: EnginePeer(group)` | boundary lift (TelegramGroup) | + +### Net accounting + +- **Drops:** 5 (4 `EnginePeer(chatPeer).compactDisplayTitle` + 1 `_asPeer()` bridge from wave 53). +- **Adds:** 5 boundary lifts (5 `EnginePeer(...)` wraps at PISPBA call sites). +- **Conversions:** 4 (`is`/`as?` → `case let`). +- **Type-site:** 2 (signature changes on PIS:3213 and PIS:7416). + +Net internal-bridge progress: `5 drops − 5 adds = 0 raw count`. But ratchet kills 4 internal display-call wraps (`EnginePeer(chatPeer).compactDisplayTitle` patterns) which is the hot path; only call-site boundary lifts remain. Closes wave-53's deferred ADD at PISPBA:851. + +--- + +## Tasks + +### Task 1: PIS signature edits + body wrap drops + +- [ ] **Step 1: Edit `openClearHistory` signature at PIS:3213** + +Replace `peer: Peer, chatPeer: Peer)` with `peer: Peer, chatPeer: EnginePeer)`. + +- [ ] **Step 2: Drop 4 `EnginePeer(chatPeer).compactDisplayTitle` wraps** + +`replace_all=true` of `EnginePeer(chatPeer).compactDisplayTitle` → `chatPeer.compactDisplayTitle`. (Only 4 occurrences in the file, all in `openClearHistory` body; verified by grep.) + +- [ ] **Step 3: Edit `ClearPeerHistory.init` signature at PIS:7416** + +Replace `peer: Peer, chatPeer: Peer, cachedData:` with `peer: Peer, chatPeer: EnginePeer, cachedData:`. + +- [ ] **Step 4: Convert PIS:7421 `is TelegramSecretChat`** + +Replace `} else if chatPeer is TelegramSecretChat {` with `} else if case .secretChat = chatPeer {`. + +- [ ] **Step 5: Convert PIS:7425 `as? TelegramGroup`** + +Replace `} else if let group = chatPeer as? TelegramGroup {` with `} else if case let .legacyGroup(group) = chatPeer {`. + +- [ ] **Step 6: Convert PIS:7436 `as? TelegramChannel`** + +Replace `} else if let channel = chatPeer as? TelegramChannel {` with `} else if case let .channel(channel) = chatPeer {`. + +- [ ] **Step 7: Convert PIS:7464 `as? TelegramUser`** + +Replace `if let user = chatPeer as? TelegramUser, user.botInfo != nil {` with `if case let .user(user) = chatPeer, user.botInfo != nil {`. + +### Task 2: PISPBA call-site lifts + bridge drop + +- [ ] **Step 1: Drop wave-53 `_asPeer()` bridge at PISPBA:851** + +Replace `chatPeer: chatPeer._asPeer()` with `chatPeer: chatPeer`. + +- [ ] **Step 2: Lift PISPBA:857 `chatPeer: user`** + +Replace `peer: user, chatPeer: user)` with `peer: user, chatPeer: EnginePeer(user))`. + +- [ ] **Step 3: Lift channel call sites (PISPBA:1067 + 1073)** + +`replace_all=true` of `chatPeer: channel` → `chatPeer: EnginePeer(channel)`. Verify exactly 2 hits flipped. + +- [ ] **Step 4: Lift group call sites (PISPBA:1234 + 1240)** + +`replace_all=true` of `chatPeer: group` → `chatPeer: EnginePeer(group)`. Verify exactly 2 hits flipped. + +### Task 3: Build verification + +- [ ] **Step 1: Run full build with `--continueOnError`.** + +Forecast 1 iteration. Risk: hidden `chatPeer` access on Peer-typed shape elsewhere (none expected — body audit complete). + +### Task 4: Commit + log + +- [ ] **Step 1: Commit wave with the two file paths explicitly.** +- [ ] **Step 2: Update `docs/superpowers/postbox-refactor-log.md` and the memory file.** +- [ ] **Step 3: Commit log.** diff --git a/docs/superpowers/plans/2026-04-25-peerinfo-enclosingpeer-engine-peer.md b/docs/superpowers/plans/2026-04-25-peerinfo-enclosingpeer-engine-peer.md new file mode 100644 index 0000000000..4e3fdf4a09 --- /dev/null +++ b/docs/superpowers/plans/2026-04-25-peerinfo-enclosingpeer-engine-peer.md @@ -0,0 +1,516 @@ +# Wave 50: enclosingPeer Peer? → EnginePeer? Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Migrate the PeerInfo members chain's `enclosingPeer` field from raw Postbox `Peer?` to `EnginePeer?` (wave 50 of the Postbox → TelegramEngine refactor). + +**Architecture:** Cross-file private struct-field migration with stored-form ratchet. Edits stay inside `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/`. Replaces `as? TelegramChannel` / `as? TelegramGroup` casts with `case let .channel(...)` / `case let .legacyGroup(...)` (wave-41/45 idiom), drops `is TelegramChannel` checks for `case .channel = ...` (wave-41 always-false-warning fix), and removes 5 internal `_asPeer()` / `EnginePeer(...)` / `flatMap(EnginePeer.init)` bridges. The engine.data subscription at PIMP:354 already returns `EnginePeer?` — this wave closes the demote-then-promote ratchet. + +**Tech Stack:** Swift, Bazel via `Make.py`, no unit tests (per `CLAUDE.md`). Verification is the full-project debug-sim-arm64 build with `--continueOnError`. + +**Iteration budget:** 1–2 (target first-pass-clean; recent first-pass-clean streak: waves 42, 43*, 45, 46, 48, 49 — *wave 43 took 2 iterations). + +**Note on TDD:** This project has no unit tests (CLAUDE.md "No tests are used at the moment"). The standard TDD test-first cycle in the skill template does not apply. Each task instead writes the edits, then verifies via Bazel build + residue grep. + +--- + +## File Structure + +| File | Role | Changes | +|---|---|---| +| `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift` (PSMI) | List-item view-model + node | Type-change stored field + init param; 4 cast/is-check rewrites; 1 `flatMap(EnginePeer.init)` simplification | +| `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift` (PIMP) | Members-pane node + helpers | 3 func sigs + 1 stored field type-change; 4 cast/is-check rewrites; 1 `EnginePeer(...)` wrap drop; 2 `_asPeer()` drops | +| `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift` (PSPB) | Profile-items builder (non-settings members section) | 1 boundary `_asPeer()` drop at the call site that constructs the migrated init | + +No public-API ripple — `PeerInfoScreenMemberItem` and `PeerInfoMembersPaneNode` are local to the PeerInfoScreen module. + +--- + +## Task 1: PSMI.swift — type changes + cast/is-check rewrites + flatMap simplification + +**Files:** +- Modify: `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift` + +**Edits in this task:** 7 (1 stored-field type, 1 init-param type, 2 cast→case-let, 2 is→case, 1 flatMap simplification). + +- [ ] **Step 1: Change stored field type at line 23** + +Find: + +```swift + let enclosingPeer: Peer? +``` + +Replace with: + +```swift + let enclosingPeer: EnginePeer? +``` + +- [ ] **Step 2: Change init parameter type at line 34** + +Find: + +```swift + enclosingPeer: Peer?, +``` + +Replace with: + +```swift + enclosingPeer: EnginePeer?, +``` + +- [ ] **Step 3: Rewrite cast at line 152 (TelegramChannel)** + +Find: + +```swift + if let channel = item.enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank) { +``` + +Replace with: + +```swift + if case let .channel(channel) = item.enclosingPeer, channel.hasPermission(.editRank) { +``` + +- [ ] **Step 4: Rewrite cast at line 154 (TelegramGroup)** + +Find: + +```swift + } else if let group = item.enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { +``` + +Replace with: + +```swift + } else if case let .legacyGroup(group) = item.enclosingPeer, !group.hasBannedPermission(.banEditRank) { +``` + +- [ ] **Step 5: Simplify flatMap at line 178** + +Find: + +```swift + let actions = availableActionsForMemberOfPeer(accountPeerId: item.context.accountPeerId, peer: item.enclosingPeer.flatMap(EnginePeer.init), member: item.member) +``` + +Replace with: + +```swift + let actions = availableActionsForMemberOfPeer(accountPeerId: item.context.accountPeerId, peer: item.enclosingPeer, member: item.member) +``` + +- [ ] **Step 6: Rewrite is-check at line 181** + +Find: + +```swift + if actions.contains(.promote) && item.enclosingPeer is TelegramChannel { +``` + +Replace with: + +```swift + if actions.contains(.promote), case .channel = item.enclosingPeer { +``` + +- [ ] **Step 7: Rewrite is-check at line 187** + +Find: + +```swift + if item.enclosingPeer is TelegramChannel { +``` + +Replace with: + +```swift + if case .channel = item.enclosingPeer { +``` + +--- + +## Task 2: PIMP.swift — signatures + stored field + body rewrites + demotion drops + +**Files:** +- Modify: `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift` + +**Edits in this task:** 11 (3 func sigs + 1 stored-field type + 4 cast/is rewrites + 1 EnginePeer wrap drop + 2 `_asPeer()` drops). + +- [ ] **Step 1: Change `func item(...)` signature at line 92** + +Find: + +```swift + func item(context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem { +``` + +Replace with: + +```swift + func item(context: AccountContext, presentationData: PresentationData, enclosingPeer: EnginePeer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem { +``` + +- [ ] **Step 2: Rewrite cast at line 113 (TelegramChannel, non-optional context)** + +Find: + +```swift + if let channel = enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank) { +``` + +Replace with: + +```swift + if case let .channel(channel) = enclosingPeer, channel.hasPermission(.editRank) { +``` + +- [ ] **Step 3: Rewrite cast at line 115 (TelegramGroup, non-optional context)** + +Find: + +```swift + } else if let group = enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { +``` + +Replace with: + +```swift + } else if case let .legacyGroup(group) = enclosingPeer, !group.hasBannedPermission(.banEditRank) { +``` + +- [ ] **Step 4: Drop the `EnginePeer(...)` wrap at line 139** + +Find: + +```swift + let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: EnginePeer(enclosingPeer), member: member) +``` + +Replace with: + +```swift + let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: enclosingPeer, member: member) +``` + +`availableActionsForMemberOfPeer` takes `peer: EnginePeer?` (PeerInfoData.swift:2314); Swift auto-wraps the non-optional `enclosingPeer: EnginePeer` to optional. + +- [ ] **Step 5: Rewrite is-check at line 142 (non-optional context)** + +Find: + +```swift + if actions.contains(.promote) && enclosingPeer is TelegramChannel { +``` + +Replace with: + +```swift + if actions.contains(.promote), case .channel = enclosingPeer { +``` + +- [ ] **Step 6: Rewrite is-check at line 148 (non-optional context)** + +Find: + +```swift + if enclosingPeer is TelegramChannel { +``` + +Replace with: + +```swift + if case .channel = enclosingPeer { +``` + +- [ ] **Step 7: Change `preparedTransition` signature at line 271** + +Find: + +```swift +private func preparedTransition(from fromEntries: [PeerMembersListEntry], to toEntries: [PeerMembersListEntry], context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> PeerMembersListTransaction { +``` + +Replace with: + +```swift +private func preparedTransition(from fromEntries: [PeerMembersListEntry], to toEntries: [PeerMembersListEntry], context: AccountContext, presentationData: PresentationData, enclosingPeer: EnginePeer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> PeerMembersListTransaction { +``` + +- [ ] **Step 8: Change stored field type at line 293** + +Find: + +```swift + private var enclosingPeer: Peer? +``` + +Replace with: + +```swift + private var enclosingPeer: EnginePeer? +``` + +- [ ] **Step 9: Drop `_asPeer()` at line 361** + +Find: + +```swift + strongSelf.enclosingPeer = enclosingPeer._asPeer() +``` + +Replace with: + +```swift + strongSelf.enclosingPeer = enclosingPeer +``` + +- [ ] **Step 10: Drop `_asPeer()` at line 363** + +Find: + +```swift + strongSelf.updateState(enclosingPeer: enclosingPeer._asPeer(), state: state, presentationData: presentationData) +``` + +Replace with: + +```swift + strongSelf.updateState(enclosingPeer: enclosingPeer, state: state, presentationData: presentationData) +``` + +- [ ] **Step 11: Change `updateState` signature at line 442** + +Find: + +```swift + private func updateState(enclosingPeer: Peer, state: PeerInfoMembersState, presentationData: PresentationData) { +``` + +Replace with: + +```swift + private func updateState(enclosingPeer: EnginePeer, state: PeerInfoMembersState, presentationData: PresentationData) { +``` + +The pass-through call sites at PIMP:275, :276, :437, :438, :451, :485 require no edit — types flow through transparently. + +--- + +## Task 3: PSPB.swift — boundary lift at members-section call site + +**Files:** +- Modify: `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift` + +**Edits in this task:** 1. + +- [ ] **Step 1: Drop `_asPeer()` at line 852** + +Find: + +```swift + items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: .account(context), enclosingPeer: peer._asPeer(), member: member, isAccount: false, action: isAccountPeer ? { _ in +``` + +Replace with: + +```swift + items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: .account(context), enclosingPeer: peer, member: member, isAccount: false, action: isAccountPeer ? { _ in +``` + +`peer` here is the closure-bound `EnginePeer` from the `data.peer` source pipeline (`PeerInfoScreenData.peer: EnginePeer?` post-wave-42, unwrapped to non-optional `EnginePeer` and being passed to a now-`EnginePeer?` param — auto-promotes to optional). + +The other `PeerInfoScreenMemberItem(...)` construction at `PeerInfoSettingsItems.swift:132` passes `enclosingPeer: nil`, which is valid for either optional type — no edit. + +--- + +## Task 4: Full-project Bazel build + +**Files:** none (verification only). + +- [ ] **Step 1: Run the build with `--continueOnError`** + +Run: + +```sh +source ~/.zshrc 2>/dev/null; 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 +``` + +Expected: clean build (`bazel build complete` or equivalent green output). + +- [ ] **Step 2: If build fails, triage iteration** + +If errors land in `PeerInfoScreenMemberItem.swift` or `PeerInfoMembersPane.swift` or `PeerInfoProfileItems.swift`: +- Read the failing line. +- Common failure modes from prior waves: + - **Always-false `is` warning under `-warnings-as-errors`**: leftover `is TelegramX` not converted in step. Re-grep `enclosingPeer is Telegram` over the 3 files. + - **Always-failing `as?` cast warning**: leftover `as? TelegramX` not converted. Re-grep `enclosingPeer.*as\?`. + - **Type mismatch on closure-capture alias**: a `strongSelf.enclosingPeer` or `self.enclosingPeer` site missed a `_asPeer()` drop. Re-grep `enclosingPeer\._asPeer\|EnginePeer\(enclosingPeer`. + - **Unused variable warning**: a binding from `case let .channel(channel)` not actually used. Re-read the body. + +Fix in place and re-run step 1. Budget: 2 iterations. + +If errors land outside those 3 files: **STOP**. The wave was supposed to be self-contained. Re-read the spec, identify the missed call site, decide whether to add it or abandon the wave. + +--- + +## Task 5: Post-edit residue grep + +**Files:** none (verification only). + +- [ ] **Step 1: Bridge residue grep** + +Run: + +```sh +grep -rnE "enclosingPeer\._asPeer|EnginePeer\(enclosingPeer\)|enclosingPeer\.flatMap\(EnginePeer" \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ +``` + +Expected: empty output. + +- [ ] **Step 2: Cast/is-check residue grep** + +Run: + +```sh +grep -rnE "enclosingPeer.*as\? TelegramChannel|enclosingPeer.*as\? TelegramGroup|enclosingPeer is TelegramChannel" \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ +``` + +Expected: empty output. + +- [ ] **Step 3: Sanity check — `enclosingPeer` references should now exclusively type-resolve to EnginePeer** + +Run: + +```sh +grep -nE ": Peer\b" submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +``` + +Expected: no `enclosingPeer: Peer` or `enclosingPeer: Peer?` annotations remain. (Other `: Peer` annotations on unrelated symbols are fine.) + +--- + +## Task 6: Commit the wave + +**Files:** none (git only). + +- [ ] **Step 1: Stage the 3 modified files** + +```sh +git add \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift +``` + +- [ ] **Step 2: Confirm staging is clean** + +```sh +git status --short | grep -v "^??" +``` + +Expected output: only the 3 staged files (lines starting with `M ` or `A `). If other modified files appear, they predate the wave (per CLAUDE.md memory: build-system/bazel-rules/sourcekit-bazel-bsp submodule marker is pre-existing WIP). + +- [ ] **Step 3: Commit** + +```sh +git commit -m "$(cat <<'EOF' +Postbox -> TelegramEngine wave 50 + +Migrate enclosingPeer Peer? -> EnginePeer? across PeerInfoScreenMemberItem ++ PeerInfoMembersPaneNode + 1 PSPB call site. 19 edits / 3 files. + +Drops 5 internal bridges: 2 _asPeer() demotions at PIMP:361/363, 1 +EnginePeer(enclosingPeer) wrap at PIMP:139, 1 flatMap(EnginePeer.init) +at PSMI:178, 1 boundary _asPeer() lift at PSPB:852. + +Closes the wave-48-pattern internal-demotion-and-external-re-promotion +ratchet at PIMP:354-363 (engine.data subscription returns EnginePeer?, +previously demoted to Peer? at storage). + +All `as? TelegramChannel` / `as? TelegramGroup` casts converted to +`case let .channel(...)` / `case let .legacyGroup(...)` (wave-41/45 +idiom). All `is TelegramChannel` checks converted to +`case .channel = ...` (wave-41 always-false-warning fix). + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +- [ ] **Step 4: Verify commit** + +```sh +git log --oneline -1 +``` + +Expected: shows the wave 50 commit. + +--- + +## Task 7: Update outcome log + memory + +**Files:** +- Modify: `docs/superpowers/postbox-refactor-log.md` +- Modify: `~/.claude/projects/-Users-isaac-build-telegram-telegram-ios/memory/project_postbox_refactor_next_wave.md` + +- [ ] **Step 1: Append wave 50 outcome to refactor log** + +Add a "Wave 50 outcome" entry at the appropriate chronological position in `docs/superpowers/postbox-refactor-log.md`. Use the wave 49 outcome entry as the template. Include: +- Commit hash (from Task 6 step 4). +- Iteration count (1 if first-pass-clean; 2 if Task 4 step 2 fired once). +- Net-bridge accounting: −5 internal bridges (2 `_asPeer()` + 1 `EnginePeer(...)` wrap + 1 `flatMap(EnginePeer.init)` + 1 boundary `_asPeer()` lift). 0 ADD wraps. 0 boundary lifts net new. +- Bazel build duration (from Task 4 step 1 output). +- Any wave-specific lessons surfaced. + +- [ ] **Step 2: Update wave-50-next-wave memory** + +Edit `~/.claude/projects/-Users-isaac-build-telegram-telegram-ios/memory/project_postbox_refactor_next_wave.md`: +- Promote wave 50 outcome line into the "Latest commits" section using the format of the wave 49 entry. +- Update the top frontmatter `description` to reflect wave 50 landed and propose wave 51. +- Promote the wave-51 candidate (`PeerInfoGroupsInCommonPaneNode.PeerEntry.peer: Peer → EnginePeer`) to the top of the "Wave 51 candidates" section, replacing the now-stale "Wave 50 candidates" header. Re-run the broader grep if needed: + +```sh +grep -rnE "^\s*(let|var|public let|public var|private let|private var) [a-zA-Z_]+: Peer\??$|^\s*(let|var|public let|public var|private let|private var) [a-zA-Z_]+: Peer\? = " \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ --include="*.swift" | grep -v "EnginePeer" +``` + +- [ ] **Step 3: Commit the doc update** + +```sh +git add docs/superpowers/postbox-refactor-log.md +git commit -m "$(cat <<'EOF' +docs: log wave 50 outcome + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +(Memory file updates are not committed — they live outside the repo.) + +--- + +## Net delta projection (from spec) + +| Category | Count | Sites | +|---|---|---| +| Internal bridge drops | −5 | PIMP:361, PIMP:363, PIMP:139, PSMI:178, PSPB:852 | +| Boundary lifts (net new) | 0 | source pipeline already EnginePeer? | +| ADD wraps | 0 | no Peer-only property accesses on bare `enclosingPeer` | +| Cast→case-let conversions | 4 | PSMI:152/154, PIMP:113/115 | +| `is`→`case` conversions | 4 | PSMI:181/187, PIMP:142/148 | +| Type annotations updated | 6 | PSMI:23/34, PIMP:92/271/293/442 | + +**Total commit footprint:** 19 line edits across 3 files, plus a docs commit for the outcome log. diff --git a/docs/superpowers/plans/2026-04-25-peerinfoscreendata-linked-peers-engine-peer.md b/docs/superpowers/plans/2026-04-25-peerinfoscreendata-linked-peers-engine-peer.md new file mode 100644 index 0000000000..8abbf566ef --- /dev/null +++ b/docs/superpowers/plans/2026-04-25-peerinfoscreendata-linked-peers-engine-peer.md @@ -0,0 +1,106 @@ +# Wave 49 — `PeerInfoScreenData.linkedDiscussionPeer` + `.linkedMonoforumPeer` `Peer? → EnginePeer?` (bundle) + +**Date:** 2026-04-25 +**Predecessor:** Wave 48 (commit `1e4c2eea33`) — savedMessagesPeer single-field migration. +**Shape:** Cross-file bundled struct-field migration (2 sibling fields, 2 files). Both fields are module-internal; no external consumer references them on `PeerInfoScreenData`. Bundled because both fields: +- Share a sibling declaration site in `PeerInfoData.swift`. +- Have parallel local-source patterns (raw `Peer?` from `peerView.peers[id]` dict lookup; **not** an engine signal as in wave 48). +- Are both consumed in `PeerInfoProfileItems.swift` only. +- Migrating one without the other adds friction at the source-construction sites where they're computed together. + +## Pre-flight inventory + +`grep -rEn "(\w+\??)\.linkedDiscussionPeer\b|(\w+\??)\.linkedMonoforumPeer\b" submodules/ Telegram/`: +- `submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift:600,651` — references are on a local `view` object (different type, NOT PeerInfoScreenData). Out of scope. +- All `data.linkedDiscussionPeer` / `data.linkedMonoforumPeer` accesses live in PIPI within the PeerInfoScreen module. + +Within scope: + +### `PeerInfoData.swift` (storage class + 2 init sites that compute the locals) + +| Site | Code | Action | +|------|------|--------| +| :396 | `let linkedDiscussionPeer: Peer?` (field decl) | Type → `EnginePeer?` | +| :397 | `let linkedMonoforumPeer: Peer?` (field decl) | Type → `EnginePeer?` | +| :453 | `linkedDiscussionPeer: Peer?,` (init param) | Type → `EnginePeer?` | +| :454 | `linkedMonoforumPeer: Peer?,` (init param) | Type → `EnginePeer?` | +| :498 | `self.linkedDiscussionPeer = linkedDiscussionPeer` | No change | +| :499 | `self.linkedMonoforumPeer = linkedMonoforumPeer` | No change | +| :1038, :1111, :1631 | `linkedDiscussionPeer: nil,` (init kwargs) | No change | +| :1039, :1112, :1632 | `linkedMonoforumPeer: nil,` (init kwargs) | No change | +| :1836 | `var discussionPeer: Peer?` (local) | Type → `EnginePeer?` | +| :1838 | `discussionPeer = peer` (where `peer = peerView.peers[linkedDiscussionPeerId]`, raw `Peer`) | Wrap → `discussionPeer = EnginePeer(peer)` | +| :1841 | `var monoforumPeer: Peer?` (local) | Type → `EnginePeer?` | +| :1843 | `monoforumPeer = peerView.peers[linkedMonoforumId]` (dict lookup, `Peer?`) | Wrap → `monoforumPeer = peerView.peers[linkedMonoforumId].flatMap(EnginePeer.init)` | +| :2131 | `var discussionPeer: Peer?` (local, parallel to :1836) | Type → `EnginePeer?` | +| :2133 | `discussionPeer = peer` (parallel to :1838) | Wrap → `discussionPeer = EnginePeer(peer)` | +| :2136 | `var monoforumPeer: Peer?` (local, parallel to :1841) | Type → `EnginePeer?` | +| :2138 | `monoforumPeer = peerView.peers[linkedMonoforumId]` (parallel to :1843) | Wrap with `.flatMap(EnginePeer.init)` | +| :1878, :1879, :2216, :2217 | init kwargs `linkedDiscussionPeer: discussionPeer,` / `linkedMonoforumPeer: monoforumPeer,` | No change (locals migrate; pass through) | + +That's **12 edits** in PID. Note the `var` declarations and assignments at :1836–:1843 and :2131–:2138 are *parallel pairs* (verified by grep). Use `replace_all=true` for the duplicate snippets. + +### `PeerInfoProfileItems.swift` (3 edits) + +| Site | Code | Action | +|------|------|--------| +| :1098 | `if let peer = data.linkedDiscussionPeer { ... }` | No change (binding works on `EnginePeer?`) | +| :1099 | `if let addressName = peer.addressName, !addressName.isEmpty {` | No change — `EnginePeer.addressName` forwarded (verified at `submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift:461`) | +| :1102 | `discussionGroupTitle = EnginePeer(peer).displayTitle(strings: ..., displayOrder: ...)` | **Drop wrap** → `peer.displayTitle(...)` | +| :1197 | `if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel {` | **Pattern rewrite** → `if case let .channel(monoforumPeer) = data.linkedMonoforumPeer {` | +| :1198 | `monoforumPeer.sendPaidMessageStars` | No change — `sendPaidMessageStars` is a `TelegramChannel` property (`SyncCore_TelegramChannel.swift:215`); `case .channel` binds to `TelegramChannel` | +| :1404 | `if let linkedDiscussionPeer = data.linkedDiscussionPeer {` | No change (binding works) | +| :1406 | `if let addressName = linkedDiscussionPeer.addressName, !addressName.isEmpty {` | No change (forwarded) | +| :1409 | `peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(...)` | **Drop wrap** → `linkedDiscussionPeer.displayTitle(...)` | + +3 edits in PIPI. + +## EnginePeer property forwarding audit + +- `EnginePeer.addressName` — forwarded at `Peer.swift:461`. ✓ +- `EnginePeer.displayTitle(strings:displayOrder:)` — defined as `EnginePeer` instance method (used elsewhere via `EnginePeer(...).displayTitle(...)` pattern; once we have an `EnginePeer`, it's directly callable). ✓ +- `case .channel` binding payload is `TelegramChannel`. ✓ +- `TelegramChannel.sendPaidMessageStars` — exists (`SyncCore_TelegramChannel.swift:215`). ✓ + +## Net bridge count + +- **ADDs (4):** boundary lifts at PID:1838 (`EnginePeer(peer)`), PID:1843 (`.flatMap(EnginePeer.init)`), PID:2133, PID:2138. These lift the Postbox-typed `peerView.peers[...]` value to the engine type at the boundary — the correct semantic position for a Postbox→Engine refactor (mirrors wave 42 where `peer.flatMap(EnginePeer.init)` lift was added at PID:1620). +- **DROPs (2):** PIPI:1102 and :1409 lose `EnginePeer(...)` wraps around `displayTitle` calls. +- **Net text bridges:** +2. **But:** the ADDs are correct boundary lifts; the field-typed-as-`EnginePeer?` is the canonical state. The 2 displayTitle DROPs are the actual ratchet value. +- **Plus:** 1 cleaner pattern (PIPI:1197 `as?` cast → `case let .channel`), no text saving but better Swift idiom. + +## Edit list + +### `PeerInfoData.swift` (12 edits, but Edit text uses `replace_all=true` to bundle parallel pairs) + +1. Line 396: `let linkedDiscussionPeer: Peer?` → `let linkedDiscussionPeer: EnginePeer?` +2. Line 397: `let linkedMonoforumPeer: Peer?` → `let linkedMonoforumPeer: EnginePeer?` +3. Line 453: `linkedDiscussionPeer: Peer?,` → `linkedDiscussionPeer: EnginePeer?,` +4. Line 454: `linkedMonoforumPeer: Peer?,` → `linkedMonoforumPeer: EnginePeer?,` +5. Lines 1836 + 2131 (`replace_all=true` over `var discussionPeer: Peer?`): → `var discussionPeer: EnginePeer?` +6. Lines 1838 + 2133 (`replace_all=true` over `discussionPeer = peer`): → `discussionPeer = EnginePeer(peer)` +7. Lines 1841 + 2136 (`replace_all=true` over `var monoforumPeer: Peer?`): → `var monoforumPeer: EnginePeer?` +8. Lines 1843 + 2138 (`replace_all=true` over `monoforumPeer = peerView.peers[linkedMonoforumId]`): → `monoforumPeer = peerView.peers[linkedMonoforumId].flatMap(EnginePeer.init)` + +### `PeerInfoProfileItems.swift` (3 edits) + +9. Line 1102: `discussionGroupTitle = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)` → `discussionGroupTitle = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)` +10. Line 1197: `if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel {` → `if case let .channel(monoforumPeer) = data.linkedMonoforumPeer {` +11. Line 1409: `peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)` → `peerTitle = linkedDiscussionPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)` + +## Out of scope + +- `PeerInfoScreenData.chatPeer` — large blast radius. Defer. +- `PeerInfoScreenMemberItem.enclosingPeer`. Defer. + +## Build & verify + +Standard Bazel command. Expected 1 iteration if forwarding audit holds; 2 if a `displayTitle` overload-resolution surprise surfaces. + +## Commit + +`Postbox -> TelegramEngine wave 49`. Body: bundle + edits summary + ADD/DROP accounting. + +## Outcome capture + +Append Wave 49 entry to `docs/superpowers/postbox-refactor-log.md`; update memory file. diff --git a/docs/superpowers/plans/2026-04-25-peerinfoscreendata-savedmessagespeer-engine-peer.md b/docs/superpowers/plans/2026-04-25-peerinfoscreendata-savedmessagespeer-engine-peer.md new file mode 100644 index 0000000000..fcbab698e1 --- /dev/null +++ b/docs/superpowers/plans/2026-04-25-peerinfoscreendata-savedmessagespeer-engine-peer.md @@ -0,0 +1,70 @@ +# 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:1313–1317` | `let savedMessagesPeer: Signal` (local) | No change — already `EnginePeer?` | +| `PeerInfoData.swift:1622` | `savedMessagesPeer: savedMessagesPeer?._asPeer(),` | **Drop bridge** → `savedMessagesPeer: 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 bridge** → `peer: 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) + +4. Line 5399: `peer: self.data?.savedMessagesPeer.flatMap(EnginePeer.init) ?? self.data?.peer,` → `peer: self.data?.savedMessagesPeer ?? self.data?.peer,` +5. 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`. diff --git a/docs/superpowers/plans/2026-04-25-phn-peer-stored-field-engine-peer.md b/docs/superpowers/plans/2026-04-25-phn-peer-stored-field-engine-peer.md new file mode 100644 index 0000000000..773f2bb01a --- /dev/null +++ b/docs/superpowers/plans/2026-04-25-phn-peer-stored-field-engine-peer.md @@ -0,0 +1,62 @@ +# Wave 47 — `PeerInfoHeaderNode.peer` stored field `Peer? → EnginePeer?` + +**Date:** 2026-04-25 +**Predecessor:** Wave 46 (commit `5ca99da5a7`) — PeerInfo avatar chain. +**Shape:** Single-file stored-field type migration. No external API change (field is `private`). + +## Target + +`submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift`, stored field `private var peer: Peer?` at line 92. + +## Pre-flight inventory + +`grep -n "self\.peer\b" PeerInfoHeaderNode.swift` returns exactly 3 references: + +| Line | Code | Site type | Action | +|------|------|-----------|--------| +| 426 | `if let peer = self.peer, peer.profileImageRepresentations.isEmpty && gallery {` | Read | None — `profileImageRepresentations` is forwarded by `EnginePeer` (see `submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift:485`). Compiles unchanged. | +| 521 | `self.peer = peer?._asPeer()` | Assignment | Drop the bridge → `self.peer = peer`. The `peer` parameter is already `EnginePeer?` after wave 45. | +| 2049–2054 | `guard let self, let peer = self.peer, ...` followed by `peer: EnginePeer(peer),` | Read | Drop the wrap at line 2054 → `peer: peer,`. | + +External access check: `grep -rn "headerNode\.peer\b" submodules/ Telegram/` returns empty. The field is private; only same-file siblings touch it. + +EnginePeer forwarding (re-confirmed at plan time): +- `profileImageRepresentations` — forwarded (Peer.swift:485). ✓ +- `EnginePeer(peer)` (PHN:2054) — accepts `EnginePeer` directly when the local is already `EnginePeer`; drop the constructor. + +Field-declaration change is the only "type" change needed. The 3 callers' adjustments are mechanical bridge drops. + +## Edit list + +1. Line 92: `private var peer: Peer?` → `private var peer: EnginePeer?` +2. Line 521: `self.peer = peer?._asPeer()` → `self.peer = peer` +3. Line 2054: `peer: EnginePeer(peer),` → `peer: peer,` + +Total: 3 edits in 1 file. + +## Out of scope + +- `PeerInfoData.swift:355,487` — different classes' `self.peer` assignments (different types). Audit confirms these are `RenderedChannelParticipant.peer` and similar — already migrated in earlier waves or owned by other types. +- `PeerInfoAvatarTransformContainerNode.peer` (line 223) — already `EnginePeer?` after wave 46. + +## Build & verify + +```sh +source ~/.zshrc 2>/dev/null; \ +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 +``` + +Expected: 1-iteration first-pass-clean. Only PeerInfoScreen + TelegramUI recompile. + +## Commit + +`Postbox -> TelegramEngine wave 47`. Body lists the 3-edit summary and notes -3 internal bridges. + +## Outcome capture + +Append a Wave 47 entry to `docs/superpowers/postbox-refactor-log.md` and update memory file `project_postbox_refactor_next_wave.md`. diff --git a/docs/superpowers/postbox-refactor-log.md b/docs/superpowers/postbox-refactor-log.md index 12f740d03c..ad0a828b79 100644 --- a/docs/superpowers/postbox-refactor-log.md +++ b/docs/superpowers/postbox-refactor-log.md @@ -1216,6 +1216,661 @@ Net consumer-surface: **−10 bridges**. TelegramCore-internal: +~12 wraps insid --- +## Wave 45 outcome (2026-04-24) + +Migrated the four `update(..., peer: Peer?, ...)` methods across the PeerInfoHeader node hierarchy — `PeerInfoHeaderNode.update`, `PeerInfoHeaderEditingContentNode.update`, `PeerInfoEditingAvatarNode.update`, `PeerInfoEditingAvatarOverlayNode.update` — from raw `Peer?` to `EnginePeer?`. Consumer-surface ratchet that drops ~15 wave-43-era ADD-WRAP bridges. Single-module wave (all four files live in `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/`). No new typealiases. No engine wrapper structs. The stored `PeerInfoHeaderNode.peer: Peer?` field stays raw (intentional scope cap) and is bridged with `peer?._asPeer()` at assignment. + +**Classification:** +- 4 signature changes: `peer: Peer?` → `peer: EnginePeer?` at PHN:496, PHECN:52, PEAN:61, PEAON:63. +- 15 DROP wraps across the four files: + - PHN body: 7 drops at :524 (listContainerNode.peer), :548 (peerInfoHeaderActionButtons), :549 (peerInfoHeaderButtons), :571 (backgroundCoverSubject = .peer), :1218 (displayTitle), :1964 (listContainerNode.update), :2361 (peerInfoIsChatMuted). + - PHECN body: 5 drops at :59, :88, :93, :159, :162 (all `canEditPeerInfo` wraps). + - PEAN body: 2 drops at :66 (canEditPeerInfo) and :88 (avatarNode.setPeer). + - PEAON body: 1 drop at :85 (canEditPeerInfo). +- 5 enum-case rewrites in PHN: `peer as? TelegramChannel` / `peer as? TelegramUser` → `case let .channel(x) = peer` / `case let .user(x) = peer` at :622, :1225, :1238, :1273, :2354. +- 7 enum-case rewrites in PHECN: `peer as? TelegramUser/Group/Channel` at :73, :77, :86 (no-bind), :91 (no-bind), :107 (inner rebind), :154. +- 1 enum-case rewrite in PEAN: `peer as? TelegramChannel` at :93. +- 1 enum-case rewrite in PEAON: `peer as? TelegramChannel` at :74. +- 4 `_asPeer()` bridges ADDED at Peer-only-property sites (wave-42 lesson pattern): + - PEAN:159 `PeerReference(peer._asPeer())` — PeerReference.init takes raw Peer. + - PEAN:166 `peer._asPeer().isCopyProtectionEnabled` — property not forwarded on EnginePeer. + - PHECN:115 `(peer?._asPeer() as? TelegramUser)?.lastName` — inline expression form kept; chose bridge over multi-line `case let` expansion. + - PHN:521 `self.peer = peer?._asPeer()` — stored raw Peer? field stays raw. +- 1 ADD wrap at PHN:363 (`itemsUpdated` closure) — closure reads stored raw `self.peer` and forwards to the migrated `PEAN.update`. +- 1 ADD bridge at PHN:1815 for `self.avatarListNode.update(..., peer: peer?._asPeer(), ...)` — `PeerInfoAvatarListNode.update(size:avatarSize:isExpanded:peer:...)` still takes raw `Peer?` (plan pre-flight missed this site). Candidate for wave 46. +- 2 external call sites in `PeerInfoScreen.swift` at :5399 and :5805 reshuffled: `peer: self.data?.savedMessagesPeer ?? self.data?.peer?._asPeer()` → `peer: self.data?.savedMessagesPeer.flatMap(EnginePeer.init) ?? self.data?.peer`. Net-zero at these sites (swap `?._asPeer()` bridge for `.flatMap(EnginePeer.init)` wrap). + +Net consumer-surface: **−10 bridges** (15 drops − 4 internal `_asPeer()` bridges − 1 internal closure ADD-WRAP at :363 − 1 :1815 bridge the plan missed; the two external call sites are net-zero). No new `import Postbox` in any consumer module; no Postbox-hygiene regression. + +**Build outcome:** 1 iteration (first-pass-clean). + +**Commit:** `6b7a23867c` (5 files, 41 insertions / 41 deletions). + +**Lessons:** + +- **Plan pre-flight grep for call sites must include internal sibling-method callers, not just the target method's direct callers.** The plan enumerated the three internal call sites of PEAN.update / PHECN.update / PEAON.update inside PeerInfoHeaderNode.update's body (:633, :1816, :1817) and the `itemsUpdated` closure (:363), plus the two external call sites (:5399, :5805). It MISSED the `self.avatarListNode.update(..., peer:...)` call at PHN:1815 — a different target node's update method (`PeerInfoAvatarListNode.update(size:avatarSize:isExpanded:peer:...)`) that happens to live on the line immediately before `self.editingContentNode.avatarNode.update(peer: peer, ...)` at :1816. Pre-flight grep token `\.update\(.+peer:` over the four files would have caught it. Fix for future pre-flight grep: for each signature migration, grep ALL `update` methods called with the migrated param and verify each target's signature — not just the siblings being migrated. + +- **Wave-43 binding-rule continues to hold at scale.** All 13 `as? TelegramUser/Group/Channel` rewrites in this wave obeyed the bind-when-used / no-bind-when-unused rule (PHECN:86 and :91 used `case .legacyGroup = peer` / `case .channel = peer` no-bind form because the branch body only appends field keys; all 11 others bind because the branch uses the concrete value). Zero `-warnings-as-errors` unused-binding risks surfaced during review. **Rule confirmed as durable.** + +- **EnginePeer forwarding audit is worth a pre-flight pass for multi-property-accessing methods.** PHN's `update` method body accesses ~15 EnginePeer-forwarded properties (id, isFake, isScam, isPremium, isVerified, debugDisplayTitle, displayTitle, addressName, effectiveProfileColor, emojiStatus, verificationIconFileId, profileImageRepresentations, etc.) plus 2 concrete-class properties requiring enum bindings (`.phone` on TelegramUser; `.info` on TelegramChannel). Without a pre-flight forwarding audit against `submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift:485-560` (the EnginePeer property-forwarding extension), iteration surprises are likely. This wave's audit at plan time caught `isCopyProtectionEnabled` as the only non-forwarded property in PHN's body — matching the single bridge in PHN. **Rule: for multi-access methods, read the EnginePeer property-forwarding extension in full at plan time; enumerate every `peer.X` site and mark forwarded vs not.** + +- **Case-pattern-against-optional is idiomatic and compiles cleanly.** All rewrites use `if case let .user(user) = peer` (or sibling case forms) against `EnginePeer?` — Swift extracts through Optional implicitly. Precedent: `PeerInfoScreenSettingsActions.swift:200` `if case let .user(user) = self.data?.peer, let phoneNumber = user.phone`. Build did not complain at any rewrite site. **Conservative fallback `if let peer, case let .x(y) = peer` is NOT needed; drop from future plans' "fallback" sections.** + +- **Bundling net-zero external-call-site migrations with net-negative internal drops is fine for ratchet-focused waves.** The two PHN.update call sites in PeerInfoScreen.swift swap one bridge form for another (`?._asPeer()` → `.flatMap(EnginePeer.init)`) — literally net-zero on wrap count. The wave accepted this because (a) the internal drops (~15) dominate, (b) migrating `savedMessagesPeer` field to remove the external wrap would expand scope to a different file's public struct field, and (c) the wave-goal was "ratchet wave-43 ADDs", which is accomplished purely via internal drops. **Heuristic: if external call sites are net-zero and the internal scope is net-negative, bundle them; don't let the external net-zero block the ratchet.** + +**Plan:** `docs/superpowers/plans/2026-04-24-peerinfoheader-update-bundle-engine-peer.md`. + +--- + +## Wave 46 outcome (2026-04-25) + +Migrated the PeerInfoScreen-local avatar chain: `PeerInfoAvatarListNode.update(peer:)` together with `PeerInfoAvatarTransformContainerNode.update(peer:)` / `.updateStoryView(peer:)` and the private `Params.peer` stored field — raw `Peer?` → `EnginePeer?` across 4 files. Ratchet wave: drops the wave-45 ADD at `PeerInfoHeaderNode.swift:1815` plus a pre-existing external bridge at `PeerInfoScreen.swift:2574`, collapses one internal wave-45 wrap inside PIATCN's `setPeer` body, and adds 2 `_asPeer()` bridges for Peer-only surfaces (`PeerReference.init` and `.isCopyProtectionEnabled`). Net: −1 bridge. The PeerInfoScreen-local `PeerInfoAvatarListNode` class shadows a same-named submodule class — the wave targets the local (PeerInfoScreen/Sources) file only; the submodule is untouched. No new typealiases. No engine wrapper structs. No `import Postbox` change. + +**Classification:** +- 4 signature changes: `peer: Peer?` → `peer: EnginePeer?` on `PeerInfoAvatarListNode.update(peer:)` and its `arguments` tuple, plus `PeerInfoAvatarTransformContainerNode.update(peer:)`, `.updateStoryView(peer:)`, and the private `Params.peer` stored field. +- 2 DROPs at call sites (ratchet-value): + - `PeerInfoHeaderNode.swift:1815` — `peer: peer?._asPeer()` → `peer: peer` (the wave-45 ADD flagged for ratchet). + - `PeerInfoScreen.swift:2574` — `peer: peer?._asPeer()` → `peer: peer` (pre-existing external bridge; direct caller of `updateStoryView`). +- 1 internal WRAP collapsed inside PIATCN's `setPeer(...)` body: `peer: EnginePeer(peer)` → `peer: peer` (type flowed once upstream migrated). +- 2 ADDs inside PIATCN body for Peer-only surface: + - PIATCN ~:404 `PeerReference(peer._asPeer())` — `PeerReference.init` takes raw `Peer`, not `EnginePeer`. + - PIATCN ~:406 `peer._asPeer().isCopyProtectionEnabled` — property defined on the `Peer` protocol (`PeerUtils.swift:236`), not forwarded on EnginePeer (same finding as wave 45 for PEAN:166). +- 2 `as? TelegramChannel` → `case let .channel(...) = peer` rewrites inside PIATCN (both `update` and `updateStoryView` bodies test `channel.isForumOrMonoForum`; the `let channel` binding is retained because the body uses the concrete value). + +Net consumer-surface: **−1 bridge** (2 drops + 1 wrap collapse − 2 adds). External API: no change. + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel build 29.587s; only PeerInfoScreen + TelegramUI recompiled. + +**Commit:** `5ca99da5a7` (4 files, 13 insertions / 13 deletions). + +**Lessons:** + +- **Shadowing-class-name pre-flight disambiguation.** `PeerInfoAvatarListNode` is defined in two places: `submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift` (the submodule) AND `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift` (the PeerInfoScreen-local class that shadows it). Wave 46's pre-flight nearly targeted the wrong file when the plan inspected the signature by class name alone. Mitigation for future waves: when the target method's signature has distinctive parameter names (here `avatarSize:isForum:threadId:threadInfo:`), grep against those distinctive tokens alongside the class name to disambiguate — and when the class name appears in multiple files, resolve the module path before writing the plan. + +- **Chain-audit extends via sibling internal methods (`updateStoryView` pattern).** The initial audit targeted only the `update(peer:)` chain. But `PeerInfoAvatarTransformContainerNode.update`'s tail calls `self.updateStoryView(peer:)` — which also takes raw `Peer?`. Expanding the wave to include `updateStoryView` was a single-file additional edit and additionally dropped the `PeerInfoScreen.swift:2574` external bridge (a direct caller of `updateStoryView`, not `update`). Pattern for future chain waves: grep the target implementation body for `self\.\w+\(.+peer:` to find internal sibling methods whose signature migration naturally bundles with the primary chain. + +- **First-pass-clean extends to chain migrations when both forwarding audit and scope audit are done at plan time.** Wave 46 is the 4th consecutive wave (42/43/44/45/46 — wave 44 needed 2 iterations) hitting 0-to-1-iteration convergence. The 1-iteration-clean pattern is reproducible when (a) the `_asPeer()` bridge sites are identified at plan time by cross-referencing the body against the EnginePeer property-forwarding extension AND `PeerReference`'s init overloads, and (b) enum-case conversions are pre-written in the plan with bindless-vs-binding form pre-classified. Both inputs were present in the wave 46 plan. + +- **Chain-bundling heuristic validated at 3 methods / 4 files.** Migrating a narrow call chain (`PeerInfoAvatarListNode.update` → `PIATCN.update` → `PIATCN.updateStoryView`) + all external call sites in a single commit is the right granularity: the internal `peer` variables flow transparently once each downstream signature migrates, and the external call-site greps (2 sites total) complete the ratchet. Net: clean atomic commit, no bridging churn remains at the boundary. For future chain waves, bundle the chain methods plus external callers in one commit; don't split by method. + +**Plan:** `docs/superpowers/plans/2026-04-25-peerinfo-avatar-chain-engine-peer.md`. + +--- + +## Wave 47 outcome (2026-04-25) + +Migrated the stored `PeerInfoHeaderNode.peer` field from `Peer?` to `EnginePeer?`. Single-file wave; field is `private`, so no external API change. 4 edits / 1 file, all inside `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift`. + +**Edits:** +- Line 92: `private var peer: Peer?` → `private var peer: EnginePeer?` +- Line 363 (`itemsUpdated` closure): `peer: EnginePeer(peer)` → `peer: peer` (drops bridge; `peer` is captured from `strongSelf.peer`). +- Line 521 (assignment in `update`): `self.peer = peer?._asPeer()` → `self.peer = peer`. +- Line 2054 (ProfileLevelInfoScreen push closure): `peer: EnginePeer(peer),` → `peer: peer,`. + +PHN:426 (`peer.profileImageRepresentations.isEmpty`) compiles unchanged because `profileImageRepresentations` is forwarded by `EnginePeer` (`Peer.swift:485`). + +Net consumer-surface: **−3 internal bridges**. + +**Build outcome:** 2 iterations. +- Iteration 1 failed at PHN:363 — pre-flight grep used `self\.peer\b` and missed the `strongSelf.peer` capture inside the `itemsUpdated` closure body. The memory file's wave-47 candidate notes had explicitly flagged PHN:363; the omission was in the executor's grep, not the plan's identification. +- Iteration 2 clean. Full-project Bazel build 28.727s; only PeerInfoScreen + TelegramUI recompiled. + +**Commit:** `d7b7536440` (4 insertions / 4 deletions in PHN.swift; plan file added). + +**Lessons:** + +- **Pre-flight grep for stored-field migrations must include closure-capture aliases (`strongSelf`, `self_`, etc.), not just `self.`.** Wave 47's first-iteration failure was 100% avoidable: the same field is referenced via `strongSelf.peer` inside an `itemsUpdated` closure, and the bare `self\.peer\b` grep missed it. For future stored-field migrations, the canonical grep pattern is `(self|strongSelf|[a-zA-Z_]*[Ss]elf)\.\b`. The convention `strongSelf` appears throughout the PeerInfo codebase whenever closures use `[weak self]`. +- **Memory-stored candidate notes can pre-list the bridge sites.** The memory file `project_postbox_refactor_next_wave.md` had explicitly named PHN:363 (`itemsUpdated` closure read), PHN:521 (stored assignment), and PHN:2054 (ProfileLevelInfoScreen push wrap) before the wave started. Reading those during plan-write would have caught the iteration-1 miss. Treat the wave-N+1 candidate notes in memory as a load-bearing input to the plan, not just narrative. +- **Single-file `private` stored-field migrations are the cleanest possible wave shape.** No external API surface, no cross-module recompilation, blast radius bounded to the same file's other methods. When a wave reaches this shape, it is a near-zero-risk drop. Future stored-field-migration waves should be explicitly classified during planning as "single-file private" / "cross-file private" / "public-surface" to set the iteration budget. + +**Plan:** `docs/superpowers/plans/2026-04-25-phn-peer-stored-field-engine-peer.md`. + +--- + +## Wave 48 outcome (2026-04-25) + +Migrated `PeerInfoScreenData.savedMessagesPeer: Peer? → EnginePeer?`. Cross-file struct-field migration contained within the PeerInfoScreen module; the field has no external consumer (`grep -rEn "(\w+\??)\.savedMessagesPeer\b"` matches only inside the PeerInfoScreen sources). 5 edits across 2 files. + +**Edits:** +- `PeerInfoData.swift:388` — field decl `Peer?` → `EnginePeer?`. +- `PeerInfoData.swift:444` — init param `Peer?` → `EnginePeer?`. +- `PeerInfoData.swift:1622` — drop `?._asPeer()` bridge. The source local `let savedMessagesPeer: Signal` (PID:1313) already produces `EnginePeer?`; the bridge was an artificial demotion. +- `PeerInfoScreen.swift:5399` and `:5805` — drop `.flatMap(EnginePeer.init)` bridge in `headerNode.update(... peer: ...)` call. The `peer` parameter of `headerNode.update` has been `EnginePeer?` since wave 45, and the `??` coalescing operand `self.data?.peer` has been `EnginePeer?` since wave 42; once the field migrates, both ends of the expression are `EnginePeer?` and the `flatMap` bridge falls out. + +The other 4 init kwarg sites (PID:1029, :1102, :1869, :2207) all pass `nil` and require no change. + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel build 29.858s; only PeerInfoScreen + TelegramUI recompiled. + +**Commit:** `1e4c2eea33` (5 insertions / 5 deletions; plan file added). + +**Lessons:** + +- **Internal-storage demotion → external re-promotion** is a high-yield ratchet pattern. The field had a `?._asPeer()` demotion at the storage site, then `.flatMap(EnginePeer.init)` re-promotions at every read. Migrating the field type drops both ends in one wave — the underlying signal pipeline never needed the `Peer?` form at all. Pattern to look for in future PSD-class migrations: any field whose initialization expression names `_asPeer()` strongly indicates that the field's source data is already `EnginePeer?`. Grep candidate fields' init expressions for `_asPeer()` to surface these high-leverage migrations. + +- **Containment audit must distinguish field declarations on different types.** The grep `grep -rln "savedMessagesPeer"` returned 6 modules outside PeerInfoScreen. All matched references were independent declarations on unrelated types (TelegramEngineMessages, ChatListUI nodes, ChatControllerContentData). The narrower regex `(\w+\??)\.savedMessagesPeer\b` filtered to actual field-access patterns and confirmed no external consumer. For future struct-field migrations, prefer the access-pattern regex over plain text grep; common field names will hit unrelated declarations and inflate apparent blast radius. + +- **`replace_all=true` is correct for verbatim-duplicated call sites.** PIS:5399 and :5805 are byte-identical `headerNode.update(...)` calls with the same `peer:` argument. Single-Edit-with-replace_all replaced both atomically. No collisions because the `headerNode.update` argument list with `flatMap(EnginePeer.init)` is sufficiently long to be unique. + +- **Wave-shape G' (sibling-of-wave-42 ratchet) revalidated.** Wave 42 migrated `PeerInfoScreenData.peer: Peer? → EnginePeer?`. Wave 48 follows the same pattern on a sibling field. Future waves on `chatPeer`, `linkedDiscussionPeer`, `linkedMonoforumPeer` will not all be this clean: `chatPeer` has 5 `as? TelegramX` checks downstream + cross-method propagation into ClearPeerHistory; `linkedMonoforumPeer` has an `as? TelegramChannel` check at PIPI:1197. Field-by-field selection should consider downstream consumer shape, not just declaration site. + +**Plan:** `docs/superpowers/plans/2026-04-25-peerinfoscreendata-savedmessagespeer-engine-peer.md`. + +--- + +## Wave 49 outcome (2026-04-25) + +Bundled migration: `PeerInfoScreenData.linkedDiscussionPeer` + `.linkedMonoforumPeer`, both `Peer? → EnginePeer?`. Cross-file struct-field migration over 2 files. Bundled because both fields share parallel local-source patterns (raw `peerView.peers[id]` dict lookup) and the same single consumer file (`PeerInfoProfileItems.swift`). The bundle is justified: the source-of-truth init blocks compute `discussionPeer` and `monoforumPeer` as a sibling pair at PID:1836–1843 and again at PID:2131–2138; migrating one without the other would leave a half-Peer-half-EnginePeer init block. + +**Edits:** + +`PeerInfoData.swift` (12 edits via `replace_all=true` on the parallel pair): +- Lines 396–397 — field decls Peer? → EnginePeer?. +- Lines 453–454 — init params Peer? → EnginePeer?. +- Lines 1836+2131 — `var discussionPeer: EnginePeer?` (parallel pair). +- Lines 1838+2133 — `discussionPeer = EnginePeer(peer)` (lift raw Peer at boundary). +- Lines 1841+2136 — `var monoforumPeer: EnginePeer?` (parallel pair). +- Lines 1843+2138 — `monoforumPeer = peerView.peers[linkedMonoforumId].flatMap(EnginePeer.init)` (lift Peer? at boundary). + +`PeerInfoProfileItems.swift` (3 edits): +- :1102 — `EnginePeer(peer).displayTitle(...)` → `peer.displayTitle(...)`. +- :1197 — `if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel` → `if case let .channel(monoforumPeer) = data.linkedMonoforumPeer`. The `case .channel` payload is `TelegramChannel`, so the downstream `monoforumPeer.sendPaidMessageStars` access at :1198 continues to compile. +- :1409 — `EnginePeer(linkedDiscussionPeer).displayTitle(...)` → `linkedDiscussionPeer.displayTitle(...)`. + +**Net bridge accounting:** +- ADDs (4): boundary lifts at PID:1838, :1843, :2133, :2138. These lift the Postbox-typed `peerView.peers[...]` dict-lookup result to the engine type at the data-flow boundary — the canonical Postbox→Engine position. Mirrors wave 42's `peer.flatMap(EnginePeer.init)` lift at PID:1620. +- DROPs (2): displayTitle `EnginePeer(...)` wraps at PIPI:1102 and :1409. +- Plus 1 idiom cleanup (PIPI:1197 — `as?` cast → enum-case pattern); no text saving but better Swift idiom. + +The +4/−2 net text-bridge count is acceptable here because the ADDs are not "internal bridges" — they're the canonical Postbox→Engine boundary that any well-typed engine field requires. Wave-tracking should distinguish "boundary lifts" (correct, permanent) from "internal bridges" (incorrect, ratchet target). + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel build 29.479s; only PeerInfoScreen + TelegramUI recompiled. Pre-flight EnginePeer-property forwarding audit (`addressName` at Peer.swift:461; `displayTitle` is an EnginePeer instance method; `case .channel` binds `TelegramChannel`; `TelegramChannel.sendPaidMessageStars` exists at SyncCore_TelegramChannel.swift:215) all verified at plan time and proved correct. + +**Commit:** `79698e4513` (15 edits across 2 files; plan added). + +**Lessons:** + +- **Bundle waves around source-of-truth coherence, not just consumer overlap.** Wave 49's bundling was justified primarily by the *source* side: both fields are computed in the same init block as a sibling pair (PID:1836–1843, :2131–2138). Migrating only one would leave the init block in a half-typed state and force an artificial bridge. Future bundling decisions should weight this "shared source-of-truth init" factor at least as heavily as "shared consumer file." + +- **Boundary-lift bridges are not "internal bridges" — wave-tracking should distinguish.** Wave-49's +4 ADDs are at the Postbox↔Engine boundary (raw `peerView.peers[...]` Postbox-typed dict → engine-typed field). They are the *correct* place for the lift; they will not ratchet away in a future wave. Internal bridges (e.g. PID:1622's `_asPeer()` in wave 48, or `EnginePeer(...)` wraps at consumer-side display calls) are the actual ratchet targets. Future wave outcome reports should categorize ADDs as "boundary lift" vs "internal bridge" to avoid confusion in net-bridge accounting. + +- **Parallel-pair `replace_all=true` works for multi-line blocks when the parallel pair is byte-identical.** PID:1836–1843 and PID:2131–2138 are byte-identical 9-line blocks. One Edit-with-replace_all applied the type+wrap rewrite to both. Pre-flight verification: `diff <(sed -n '1836,1843p' file) <(sed -n '2131,2138p' file)` returned empty before the edit. Pattern generalizes to any "compute the same locals in two adjacent init paths" code structure (a common shape in PeerInfoData.swift's signal pipelines). + +- **Whitespace nuance with `replace_all` multi-line edits.** The blank separator between the two `var X: Peer?` blocks had trailing spaces (Swift code with whitespace-trimming editor settings). The new_string used a clean blank line; the resulting file has a plain `\n` separator instead of ` \n`. Cosmetic only — no compile impact — but worth noting for future plans: when the indentation-sensitive pre-text uses trailing whitespace, mirror it exactly in the new_string to avoid inadvertent normalization. + +- **`case .channel` pattern at field-binding compiles cleanly against `EnginePeer?` (re-confirmed wave 45 lesson).** No `if let ... case let .channel = ...` two-step needed; single-step `if case let .channel(monoforumPeer) = data.linkedMonoforumPeer` works directly. + +**Plan:** `docs/superpowers/plans/2026-04-25-peerinfoscreendata-linked-peers-engine-peer.md`. + +--- + +## Wave 50 outcome (2026-04-25) + +`enclosingPeer: Peer? → EnginePeer?` migration across the PeerInfo members chain. 19 edits across 3 files (`PeerInfoScreenMemberItem.swift`, `PeerInfoMembersPane.swift`, `PeerInfoProfileItems.swift`). Cross-file private struct-field migration with stored-form ratchet (wave-47 taxonomy: "cross-file private"). Closes the wave-48-pattern internal-demotion-and-external-re-promotion ratchet at PIMP:354–363, where `engine.data.subscribe(...)` produced an `EnginePeer?` that was demoted to `Peer?` for storage and then re-promoted at every consumer. + +**Edits:** + +`PeerInfoScreenMemberItem.swift` (7 edits): +- :23 — stored field `let enclosingPeer: Peer? → EnginePeer?`. +- :34 — init param `Peer? → EnginePeer?`. +- :152, :154 — `as? TelegramChannel` / `as? TelegramGroup` → `case let .channel(channel)` / `case let .legacyGroup(group)`. +- :178 — `peer: item.enclosingPeer.flatMap(EnginePeer.init)` → `peer: item.enclosingPeer` (auto-promotes to `EnginePeer?`). +- :181, :187 — `is TelegramChannel` → `case .channel = ...` (wave-41 always-false-warning fix). + +`PeerInfoMembersPane.swift` (11 edits): +- :92, :271, :442 — three func sigs `enclosingPeer: Peer → EnginePeer`. +- :113, :115 — `as? TelegramChannel` / `as? TelegramGroup` → `case let .channel(channel)` / `case let .legacyGroup(group)`. +- :139 — `peer: EnginePeer(enclosingPeer)` → `peer: enclosingPeer` (drop wrap, auto-promotes). +- :142, :148 — `is TelegramChannel` → `case .channel = ...`. +- :293 — stored field `private var enclosingPeer: Peer? → EnginePeer?`. +- :361 — `strongSelf.enclosingPeer = enclosingPeer._asPeer()` → `strongSelf.enclosingPeer = enclosingPeer`. +- :363 — `strongSelf.updateState(enclosingPeer: enclosingPeer._asPeer(), ...)` → `..., enclosingPeer: enclosingPeer, ...`. + +`PeerInfoProfileItems.swift` (1 edit): +- :852 — `enclosingPeer: peer._asPeer()` → `enclosingPeer: peer` (boundary lift; `peer` is already `EnginePeer` from the closure scope's `data.peer` post-wave-42). + +**Net bridge accounting:** +- DROPs (5): 2× `_asPeer()` demotion (PIMP:361, :363), 1× `EnginePeer(...)` wrap (PIMP:139), 1× `flatMap(EnginePeer.init)` (PSMI:178), 1× boundary `_asPeer()` lift (PSPB:852). +- ADDs (0): no new bridges. Pattern conversions (`as?` → `case let`, `is` → `case`) are not bridges; they're idiom shifts mandated by the EnginePeer enum representation. +- Pass-through call sites at PIMP:275, :276, :437, :438, :451, :485 needed no edits — types flow transparently through stored field, local var, and func params after the signature changes. + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel build 29.290s; only PeerInfoScreen + TelegramUI recompiled. Continues the wave 42/45/46/48/49 first-pass-clean streak (6 of last 8 waves first-pass-clean; wave 44 was 2 iterations, wave 47 was 2 iterations). + +**Commit:** `a1b77bcf74` (19 edits across 3 files). + +**Lessons:** + +- **The wave-48 internal-demotion ratchet pattern is reliably first-pass-clean.** When the source signal already produces the engine type and is being demoted at storage, migrating the storage to the engine type is a 1-iteration target if the consumer's only Peer-only access is through `as? TelegramX` / `is TelegramX` (mechanical conversions to `case let`/`case`). Wave 50 reproduces the wave-48 pattern verbatim and lands the same first-pass-clean outcome. + +- **Pre-flight EnginePeer-property forwarding audit confirmed unnecessary for `case let`-bound concrete types.** The `case let .channel(channel)` / `case let .legacyGroup(group)` patterns bind a `TelegramChannel` / `TelegramGroup` directly — methods like `.hasPermission(.editRank)` and `.hasBannedPermission(.banEditRank)` are class methods on the concrete type and call cleanly without `_asPeer()`. The forwarding audit in earlier waves was load-bearing for properties accessed *off the bare EnginePeer* (e.g., `peer.displayTitle` requiring an EnginePeer method); when accessed off a case-bound concrete type, no audit is needed. + +- **Bundling rationale: PSMI + PIMP + PSPB:852 share the same data-flow contour.** `PeerInfoProfileItems.swift:852` is the only producer of `PeerInfoScreenMemberItem` in the migration's scope (PSI:132 also produces but with `enclosingPeer: nil`, no edit needed). PIMP and PSMI form a tight pane–node pair. The 1-edit PSPB inclusion was non-controversial because the PSPB:852 site is the canonical Postbox→Engine boundary above the migrated chain. Boundary-lift inclusion for a 1-edit site is correct. + +- **Bundled implementer dispatch with a single subagent is the right shape for a 19-edit / 3-file mechanical wave.** Per-task subagent dispatch (1 task per file) would have triggered three implementer roundtrips and three review rounds for what is essentially one cohesive editing unit that doesn't pass build at any per-file checkpoint. The subagent-driven-development skill's "fresh subagent per task" convention adapts to "fresh subagent per cohesive editing unit" when individual tasks are not independently verifiable. + +**Plan:** `docs/superpowers/plans/2026-04-25-peerinfo-enclosingpeer-engine-peer.md`. + +--- + +## Wave 51 outcome (2026-04-25) + +`GroupsInCommonListEntry.peer: Peer → EnginePeer` migration in `PeerInfoGroupsInCommonPaneNode.swift` (single-file private struct-field). 7 edits, 1 file, **first-pass-clean**. Wave-shape: narrow internal struct-field migration with deliberate boundary scoping — public init's `openPeerContextAction: (Bool, Peer, …)` field left unmigrated to avoid cascading into `PeerInfoPaneContainerNode` (parent), `PeerInfoRecommendedPeersPaneNode` (sibling pane sharing the closure type), and upstream callers in `PeerInfoScreen.swift`. Saved for a coordinated wave once a wider closure-type sweep is justified. + +**Edits:** + +`PeerInfoGroupsInCommonPaneNode.swift` (7 edits): +- :28 — stored field `var peer: Peer → var peer: EnginePeer`. +- :35 — `lhs.peer.isEqual(rhs.peer)` → `lhs.peer == rhs.peer` (EnginePeer is `Equatable`; was Peer-protocol method). +- :42 — `item()` closure params `(Peer) -> Void` → `(EnginePeer) -> Void` and `(Peer, ASDisplayNode, ContextGesture?) -> Void` → `(EnginePeer, ASDisplayNode, ContextGesture?) -> Void`. +- :44 — `peer: EnginePeer(self.peer)` → `peer: self.peer` (drop wrap; `ItemListPeerItem.peer:` already takes `EnginePeer`). +- :54 — `preparedTransition()` closure params, same migration as :42. +- :232 — `GroupsInCommonListEntry(... peer: peer)` → `peer: EnginePeer(peer)` (boundary lift; `peer.peer` source is `RenderedPeer.peer: Peer?`). +- :236 — `self?.chatControllerInteraction.openPeer(EnginePeer(peer), …)` → `self?.chatControllerInteraction.openPeer(peer, …)` (drop wrap; `peer` is now `EnginePeer` from migrated closure param). +- :238 — `self?.openPeerContextAction(false, peer, node, gesture)` → `self?.openPeerContextAction(false, peer._asPeer(), node, gesture)` (internal bridge to still-Peer-typed stored field at PIGCP:68/109). + +**Net bridge accounting:** +- DROPs (2): 1× `EnginePeer(...)` wrap (PIGCP:44 → ItemListPeerItem), 1× `EnginePeer(...)` wrap (PIGCP:236 → chatControllerInteraction.openPeer). +- ADDs (2): 1× boundary lift `EnginePeer(peer)` (PIGCP:232; `RenderedPeer.peer: Peer?` → struct field; correct/permanent), 1× internal `_asPeer()` bridge (PIGCP:238) to the still-`Peer`-typed stored `openPeerContextAction` field. +- Net internal bridges: **−1**. Boundary lifts: **+1**. + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel build 29.454s; PeerInfoScreen + TelegramUI recompiled. Continues the first-pass-clean streak (waves 42, 45, 46, 48, 49, 50, 51 — 7 of last 9 waves first-pass-clean; waves 44 and 47 each took 2 iterations). + +**Commit:** `f2b67a1b54` (7 edits in 1 file). + +**Lessons:** + +- **Narrow-wave shape works for struct-field migrations even when the field flows into closure params.** The internal closures (`item()`, `preparedTransition()`) can be migrated independently from the public init's stored closure field, by sandwiching the still-Postbox-typed boundary at the closure that captures the stored field. This adds one `_asPeer()` bridge at the boundary but keeps the wave atomic and first-pass-clean. +- **Boundary-lift accounting clarifies wave value.** Net internal-bridge count (−1) is the correct progress metric; boundary lifts (+1) at `RenderedPeer.peer` are permanent until `RenderedPeer → EngineRenderedPeer` lands. The wave is a real ratchet step even with net-zero raw bridge count. +- **Memory-stored field name was wrong (memory said `PeerEntry`, actual was `GroupsInCommonListEntry`).** Verify struct identifiers at plan time. Doesn't affect the wave but should keep memory accurate for future planning. + +--- + +## Wave 52 outcome (2026-04-25) + +`PeerInfoPaneContainerNode.openPeerContextAction` closure parameter `Peer → EnginePeer` cascade across `PeerInfoPaneContainerNode` (PIPC), `PeerInfoGroupsInCommonPaneNode` (PIGCP), `PeerInfoRecommendedPeersPaneNode` (PIRP), and `PeerInfoScreen.swift` (PIS) — closes the wave-51 PIGCP:238 `_asPeer()` bridge and unifies the closure type across both sibling panes. 4 files, 8 type-site edits + 5 drops, **first-pass-clean**. + +**Edits:** + +- `PeerInfoPaneContainerNode.swift` (2): :411 init param closure type, :640 stored field closure type — both `(Bool, Peer, …) → (Bool, EnginePeer, …)`. +- `PeerInfoGroupsInCommonPaneNode.swift` (3): :68 stored field, :109 init param, :238 dropped `peer._asPeer()` (the wave-51 bridge — closed in this wave). +- `PeerInfoRecommendedPeersPaneNode.swift` (5): :68 inner item closure, :88 dropped `peer._asPeer()`, :94 `preparedTransition` closure, :119 stored field, :155 init param. +- `PeerInfoScreen.swift` (3): :1331 dropped `EnginePeer(peer)` wrap (`chatInterfaceInteraction.openPeer`), :1340 dropped `EnginePeer(peer)` wrap (`joinChannel(peer:)`), :1348 dropped `EnginePeer(peer)` wrap (second `chatInterfaceInteraction.openPeer`). + +**Net bridge accounting:** +- DROPs (5): 2× internal `_asPeer()` (PIGCP:238 + PIRP:88), 3× `EnginePeer(peer)` wrap (PIS:1331 / :1340 / :1348). +- ADDs (0). The four forwarding closures (PIPC:1044–1045, PIRP:273–274 / :303–304, PIS:1319) all use parameter type inference, so the closure-type cascade ripples through automatically without explicit edits at those sites. +- Net internal bridges: **−5**. No boundary lifts (the migration unifies closure types end-to-end inside the module; receiving APIs already accepted `EnginePeer`). + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel build 29.52s. Streak continues — 8 of last 10 waves first-pass-clean (42, 45, 46, 48, 49, 50, 51, 52; waves 44 and 47 each took 2 iterations). + +**Commit:** `c86aa1aba3` (4 files, 13 insertions, 13 deletions). + +**Lessons:** + +- **Closure-type cascade as a wave-shape variant of struct-field migration.** When the migrating type is a closure parameter (not a struct field), Swift's parameter-type inference at every forwarding-closure site does the cascade automatically — no explicit edits needed at type-inferred sites. Wave 52 had 5 inferred-cascade sites (PIPC:1044, PIRP:273/303, PIS:1319) and only 8 explicit type-site edits (init params + stored fields). Compare to wave 50 (struct-field cascade with 19 explicit edits). +- **Pre-flight receiving-API verification is load-bearing for closure-type migrations.** Wave 52's plan verified `chatControllerInteraction.openPeer: (EnginePeer, …)` and `joinChannel(peer: EnginePeer)` BEFORE writing the wave, ensuring the 3 PIS body wraps would drop without compensating ADDs. If either API still took raw `Peer`, the migration would have re-introduced wraps inside the closure body, defeating the wave's purpose. +- **The wave-51 deferral was the correct call.** Wave 51 explicitly noted `openPeerContextAction: (Bool, Peer, …)` would NOT migrate within that wave's scope. Wave 52 closed it cleanly with a 4-file unit. This validates the wave-47 lesson: "memory-stored wave-N+1 candidate notes are load-bearing plan input." The deferral pre-classified the candidate, and wave 52 picked it up directly. +- **Bundled implementer dispatch with Haiku confirmed for closure-type cascades.** Wave 50 established Haiku-sufficiency for mechanical wave-shape edits (19 edits). Wave 52 reproduces with 13 edits — implementer applied all edits byte-perfectly first attempt, all greps passed, build first-pass-clean. Total cost ~56k implementer tokens. + +--- + +## Wave 53 outcome (2026-04-25) + +`PeerInfoScreenData.chatPeer: Peer? → EnginePeer?` field migration with deliberately narrow scope (defers `ClearPeerHistory.init.chatPeer: Peer` and `openClearHistory.chatPeer: Peer` to a future wave). 3 files / 14 insertions / 14 deletions / **first-pass-clean** Bazel ~29.5s. + +**Edits:** + +`PeerInfoData.swift` (PSD, 6 edits): +- :387 — field decl `let chatPeer: Peer? → let chatPeer: EnginePeer?`. +- :443 — init param `chatPeer: Peer? → chatPeer: EnginePeer?`. +- :1028, :1621, :1868, :2206 — boundary lifts at PSD-internal init call sites: `chatPeer: peer → chatPeer: peer.flatMap(EnginePeer.init)` (1028), `chatPeer: peerView.peers[peerId/groupId] → chatPeer: peerView.peers[...].flatMap(EnginePeer.init)` (1621/1868/2206). Each matches the sibling `peer:` line on the same construction. + +`PeerInfoScreenOpenChat.swift` (PISOC, 2 edits): +- :25 — `chatLocation: .peer(EnginePeer(peer)) → chatLocation: .peer(peer)` (drop wrap). +- :89 — same pattern (drop wrap). + +`PeerInfoScreenPerformButtonAction.swift` (PISPBA, 6 edits): +- :428 — `if let secretChat = chatPeer as? TelegramSecretChat → if case let .secretChat(secretChat) = chatPeer`. +- :431 — `} else if let group = chatPeer as? TelegramGroup → } else if case let .legacyGroup(group) = chatPeer`. +- :435 — `} else if let user = chatPeer as? TelegramUser → } else if case let .user(user) = chatPeer`. +- :439 — `} else if let channel = chatPeer as? TelegramChannel → } else if case let .channel(channel) = chatPeer`. +- :463 — `if let channel = chatPeer as? TelegramChannel → if case let .channel(channel) = chatPeer` (separate `if`, not in the else-if chain). +- :851 — `chatPeer: chatPeer → chatPeer: chatPeer._asPeer()` (1× ADD `_asPeer()` boundary bridge for unmigrated `ClearPeerHistory.init.chatPeer: Peer`). + +**Net bridge accounting:** +- DROPs (2): `EnginePeer(peer)` wrap at PISOC:25 + PISOC:89. +- ADDs (1): `_asPeer()` at PISPBA:851 (boundary with unmigrated `ClearPeerHistory.init`). +- Boundary lifts (4): PSD:1028, :1621, :1868, :2206 — `Peer? → EnginePeer?` at the data-flow boundary into the migrated struct field. Permanent until upstream `RenderedPeer.peer / peerView.peers` migrate. +- Net internal bridges: **−1**. + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel ~29.5s. Streak: 9 of last 11 waves first-pass-clean (42, 45, 46, 48, 49, 50, 51, 52, 53; waves 44 and 47 each took 2 iterations). + +**Commit:** `438b4d7f46` (3 files, 14/14 line changes). + +**Lessons:** + +- **Narrow-scope is the right wave shape when bundling produces net-negative accounting.** Bundling wave 53 with `ClearPeerHistory.init` + `openClearHistory` migrations would have dropped 4 `EnginePeer(chatPeer)` wraps in `openClearHistory` body BUT added 4–6 `EnginePeer(...)` boundary lifts at PISPBA call sites (where `channel`/`group`/`user` Peer locals get passed). Pre-flight call-site classification turned this from "tempting bundle" into "net-negative bundle" — the deferral is correct. +- **`as? Telegram*` cluster conversion is mechanically safe even with `else if` chains.** 5 conversions in the same `.more` case body, all sharing the `chatPeer` scrutinee. Each `case let .x(y) = chatPeer` has the same scope semantics as the original `let y = chatPeer as? X` — `y` shadows nothing in the surrounding scope, control flow is preserved, and `else if` chains over EnginePeer's 4-case enum are equivalent to the original sequential `as?` casts. +- **Boundary-lift uniformity check.** Each `chatPeer:` PSD construction site already had a sibling `peer:` line one above with `peer.flatMap(EnginePeer.init)` or `peerView.peers[...].flatMap(EnginePeer.init)`. The wave-53 edits are byte-identical to the sibling pattern. Pre-flight verification ("does the sibling line use the same construction?") is the simplest signal that boundary lifts are idiomatic for the surrounding code. +- **Memory's "30+ sites / multi-iter" overcount.** Memory's wave-53 estimate captured the BUNDLED scope; narrow scope reduces to 14 sites / 1-iteration. Future memory updates should distinguish "bundled candidate site count" from "narrow candidate site count" so wave-N+1 budgets aren't anchored too high. + +--- + +## Wave 54 outcome (2026-04-25) + +`ClearPeerHistory.init.chatPeer` + `openClearHistory.chatPeer` `Peer → EnginePeer` bundled method-signature migration. Closes wave-53's deferred sibling. 2 files / 16 insertions / 16 deletions / **first-pass-clean** Bazel 31.484s. Commit `e3da090a7f`. + +**Edits:** + +`PeerInfoScreen.swift` (PIS, 10 edits): +- :3213 — `openClearHistory(... chatPeer: Peer) → ... chatPeer: EnginePeer)`. +- :3230, :3232, :3251, :3269 — drop 4 internal display-call wraps `EnginePeer(chatPeer).compactDisplayTitle → chatPeer.compactDisplayTitle` (the hot-path ratchet target). +- :7416 — `ClearPeerHistory.init(... chatPeer: Peer, cachedData:) → ... chatPeer: EnginePeer, cachedData:`. +- :7421 — `} else if chatPeer is TelegramSecretChat { → } else if case .secretChat = chatPeer {` (no-bind pattern; case body uses no fields from the secretChat). +- :7425 — `} else if let group = chatPeer as? TelegramGroup { → } else if case let .legacyGroup(group) = chatPeer {`. +- :7436 — `} else if let channel = chatPeer as? TelegramChannel { → } else if case let .channel(channel) = chatPeer {`. +- :7464 — `if let user = chatPeer as? TelegramUser, user.botInfo != nil { → if case let .user(user) = chatPeer, user.botInfo != nil {`. + +`PeerInfoScreenPerformButtonAction.swift` (PISPBA, 6 edits): +- :851 — DROP wave-53 ADD: `chatPeer: chatPeer._asPeer() → chatPeer: chatPeer`. +- :857 — boundary lift: `chatPeer: user → chatPeer: EnginePeer(user)` (TelegramUser local from `case let .user(user)` upstream). +- :1067, :1073 — `chatPeer: channel → chatPeer: EnginePeer(channel)` (TelegramChannel locals). +- :1234, :1240 — `chatPeer: group → chatPeer: EnginePeer(group)` (TelegramGroup locals). + +**Net bridge accounting:** +- DROPs (5): 4 internal `EnginePeer(chatPeer).compactDisplayTitle` wraps in PIS:openClearHistory body + 1 `_asPeer()` bridge at PISPBA:851 (the wave-53 ADD). +- ADDs (5): 5 boundary `EnginePeer(...)` lifts at PISPBA call sites. +- Conversions (4): `is`/`as?` → `case let` on PIS:7421/7425/7436/7464. +- Type-site (2): signature changes on PIS:3213 and PIS:7416. +- Net internal bridges: **0 raw count** — but the **ratchet kills 4 internal display-call wraps in the hot path** (PIS:3230/3232/3251/3269); only call-site boundary lifts remain, and those are permanent until upstream PISPBA sites get further migrated (e.g., to flow EnginePeer locals end-to-end). + +**Build outcome:** 1 iteration (first-pass-clean). Full-project Bazel 31.484s. Streak: 10 of last 12 waves first-pass-clean (42, 45, 46, 48, 49, 50, 51, 52, 53, 54; waves 44 and 47 each took 2 iterations). + +**Lessons:** + +- **Bundled deferral closure cleanly inverts the wave-53 narrow-scope decision.** Wave 53 deferred this bundle because the call-site classification at that time was "5 boundary lifts vs. 4 wrap drops = net-negative" — but the analysis missed that closing wave-53's 1 ADD is also a drop. The actual full accounting at wave-54 time: 5 drops (4 wraps + wave-53 ADD) vs. 5 lifts (1 user + 2 channel + 2 group) = 0 raw count, with ratchet benefit of 4 hot-path wraps killed. Wave-by-wave deferral followed by closure is the right shape when the bundle's first half is independently valuable (wave 53 dropped 2 PISOC wraps and migrated the field type on its own merit). +- **`case .secretChat = chatPeer` (no-bind pattern) compiles cleanly under `-warnings-as-errors`.** No "unused binding" warning because there is no binding. The original `chatPeer is TelegramSecretChat` form is structurally similar — both are predicate checks with no name to discard. +- **`peer:` parameter unused in body is fine for `-warnings-as-errors`.** `openClearHistory(... peer: Peer, ...)` body never references `peer`; this passed wave 53 and continues to pass wave 54. Function-parameter unused warnings are not enabled in this codebase's compile flags. Confirms a wave-53 implicit assumption. +- **Bundled signature migration with mechanical `as?` cluster + small call-site count = 1-iteration target.** 2 files, 16 edits, 4 case-let conversions, 5 boundary lifts, 1 wave-53-ADD drop, 4 hot-path wrap drops. Full-project Bazel completed in one shot. + +--- + +## Wave 55 outcome (2026-04-25) + +`PeerInfoScreenViewControllerNode.deletePeerChat(peer:)` private-method first-arg `Peer → EnginePeer`. 1 file / 4 edits / **first-pass-clean** Bazel 29.284s. Commit `4818a0f090`. + +**Edits:** all in `PeerInfoScreen.swift`: +- :4502 — drop `_asPeer()` at `openDeletePeer` ActionSheet button: `self?.deletePeerChat(peer: peer._asPeer(), globally: true) → self?.deletePeerChat(peer: peer, globally: true)`. +- :4556 — same drop at `openLeavePeer` TextAlertAction. +- :4564 — definition signature `peer: Peer → peer: EnginePeer`. +- :4573 — drop internal wrap inside body: `EngineRenderedPeer(peer: EnginePeer(peer)) → EngineRenderedPeer(peer: peer)` passed to `chatListController.maybeAskForPeerChatRemoval`. + +**Net:** 3 drops, 0 adds. Both call sites' `peer` is `EnginePeer` (sourced from `engine.data.get(...Item.Peer.Peer(id:))` returning `Signal`). Hot-path bridges eliminated end-to-end from delete/leave entry points down to `maybeAskForPeerChatRemoval`. Streak: 11 of last 13 first-pass-clean. + +**Lessons:** + +- **Single-file private-method migration with both call sites already EnginePeer = pure-drop wave shape.** The `_asPeer()` bridge at the call site exists *because* the receiving method was Peer-typed; once both endpoints are migrated, the bridge becomes deletable in the same wave. Pre-flight grep for `(.*\._asPeer\(\))` patterns surfaces this shape immediately. + +--- + +## Wave 56 outcome (2026-04-25) + +`PeerInfoInteraction.openPeerInfo` closure-type and `PeerInfoScreenViewControllerNode.openPeerInfo` private-method first-arg `Peer → EnginePeer` cascade. 3 files / 7 edits / **first-pass-clean** Bazel 29.074s. Commit `31433fc1d4`. + +**Edits:** + +`PeerInfoInteraction.swift` (PII, 2 edits): +- :45 — `let openPeerInfo: (Peer, Bool) -> Void → (EnginePeer, Bool) -> Void`. +- :123 — same closure type in init param. + +`PeerInfoScreen.swift` (PIS, 2 edits): +- :4304 — `private func openPeerInfo(peer: Peer, ...) → ... peer: EnginePeer`. +- :4306 — drop internal `EnginePeer(peer)` wrap (passed to `makePeerInfoController` which already takes EnginePeer post-wave-39). +- :1482 — boundary lift: `strongSelf.openPeerInfo(peer: member.peer, ...) → ... peer: EnginePeer(member.peer)` (`PeerInfoMember.peer` is still `Peer`, depends on RenderedPeer foundational migration). + +`PeerInfoProfileItems.swift` (PIPI, 2 edits): +- :524 — drop `_asPeer()` bridge: `interaction.openPeerInfo(managedByBot._asPeer(), false) → interaction.openPeerInfo(managedByBot, false)` (`managedByBot` is EnginePeer? from PSD field). +- :860 — boundary lift: `interaction.openPeerInfo(member.peer, true) → interaction.openPeerInfo(EnginePeer(member.peer), true)`. + +**Net:** 2 drops (PIS:4306 wrap + PIPI:524 _asPeer bridge), 2 boundary lifts (PIS:1482 + PIPI:860 at PeerInfoMember.peer source sites). Net raw count = 0 but 1 hot-path bridge eliminated. The 2 boundary lifts will close when `PeerInfoMember.peer` migrates (depends on RenderedPeer foundational session). Streak: 12 of last 14. + +**Lessons:** + +- **PIS:520-521 lambda forwarding `peer, isMember in self?.openPeerInfo(...)` needs no edit when the closure-field type migrates.** Swift parameter-type inference cascades from the migrated closure-field type to the lambda's parameter, and the lambda body forwards an EnginePeer to the migrated method. This is the same lesson as wave 52's PIPC.openPeerContextAction cascade — when the lambda's only role is to forward the parameter, the type re-bind is invisible at the lambda site. +- **Net-zero migrations are still worth doing when they consolidate a hot path.** Even though wave 56 has 2 drops and 2 adds, the 2 adds are at sources that will likely migrate later (PeerInfoMember.peer foundational), and the 2 drops include a hot-path `_asPeer()` ratchet at PIPI:524. The migration also makes the chain (`interaction.openPeerInfo → self.openPeerInfo → makePeerInfoController`) cleanly EnginePeer end-to-end, so future migrations can reason about it without bridge-hopping. + +--- + +## Wave 57 outcome (2026-04-25) + +Add `EnginePeer.isCopyProtectionEnabled` forwarding property in TelegramCore + drop 4 consumer-side `_asPeer().isCopyProtectionEnabled` bridges. 5 files / 5 edits / **first-pass-clean** Bazel 237.868s (cascade compile from TelegramCore touch). Commit `a5fc9fcf0e`. + +**Edits:** + +`TelegramCore/Sources/TelegramEngine/Peers/Peer.swift` (1 edit, 1 addition): +- Appended `var isCopyProtectionEnabled: Bool { return self._asPeer().isCopyProtectionEnabled }` to the existing `public extension EnginePeer { ... }` block (sibling to isDeleted, isScam, isVerified, isPremium). + +Consumer drops (4 edits across 4 files): +- `PeerInfoEditingAvatarNode.swift:166` — `peer._asPeer().isCopyProtectionEnabled → peer.isCopyProtectionEnabled` (NativeVideoContent.captureProtected). +- `PeerInfoAvatarTransformContainerNode.swift:406` — same. +- `PeerInfoData.swift:2259` — `peerInfoIsCopyProtected(data:)` body. +- `PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift:128` — `canShare = !peer.isCopyProtectionEnabled` (peer from `case let .image(_, _, _, _, peer, _, _, _, _, _, _, _) = entry`, which is `EnginePeer?`). + +**Net:** 4 internal `_asPeer()` bridge drops; 0 adds. Other `_asPeer()` calls at adjacent lines (e.g., `PeerReference(peer._asPeer())` at PIED:159 / PIATCN:404) remain — `PeerReference.init` still takes raw Postbox Peer. + +**Cascade compile:** full-project Bazel 237.868s (vs. ~30s typical) due to TelegramCore touch triggering downstream module rebuilds. Behavior parity verified by build success. + +**Lessons:** + +- **EnginePeer forwarding-extension addition is a high-leverage wave shape when ≥3 consumer sites use the same `_asPeer().` pattern.** Cost: 1 line in TelegramCore. Benefit: drops N consumer-side bridges (4 in this wave) and unlocks future call-site simplifications that pattern-match on the property. The wave-26 lesson "EnginePeer forwarding audit is load-bearing for multi-property methods" extends to "single-property forwarding is a wave shape on its own when the access-count threshold is met." +- **TelegramCore touches incur cascade-recompile cost (~210s extra wall-clock).** Worth budgeting; not a blocker. Future TelegramCore-touching waves should weight cascade time as part of wave-shape planning. +- **`EnginePeer` cases (.user/.legacyGroup/.channel/.secretChat) all preserve the underlying Postbox Peer's flag access via `_asPeer()`** — the forwarding implementation is a one-line wrapper. No case-by-case logic needed for properties that already exist on `Peer` protocol's extension. Rule of thumb: any `var ` on `extension Peer` in PeerUtils.swift can be forwarded mechanically. + +--- + +## Wave 58 outcome (2026-04-25) + +`AccountContext.openAddPeerMembers` + `presentAddMembersImpl` cross-module `groupPeer: Peer → EnginePeer` migration. 6 files / 9 edits / **2 build iterations**. Commit `261c086c15`. Bazel 180.751s (iter 1, failure with 1 error) + 29.946s (iter 2, clean) = ~210s wall-clock. + +**Edits:** + +Protocol + impl (2 edits, 2 files): +- `AccountContext.swift:1456` — protocol method `groupPeer: Peer → groupPeer: EnginePeer`. +- `SharedAccountContext.swift:2351` — implementation signature. + +`PresentAddMembers.swift` (4 edits, 1 file): +- :14 — public function signature. +- :38 — `if let group = groupPeer as? TelegramGroup → if case let .legacyGroup(group) = groupPeer`. +- :47 — `} else if let channel = groupPeer as? TelegramChannel, ... → } else if case let .channel(channel) = groupPeer, ...`. +- :210 — **iter-2 fix:** drop now-redundant `EnginePeer(groupPeer)` wrap inside body — `peer: EnginePeer(groupPeer) → peer: groupPeer` (after parameter is EnginePeer, wrapping it again doesn't compile). + +Call sites (3 edits, 3 files): +- `PeerInfoScreen.swift:4606` — drop `_asPeer()`: `groupPeer: groupPeer._asPeer() → groupPeer: groupPeer` (`groupPeer = data.peer` is EnginePeer? post-wave-42). +- `ChatListUI/Sources/ChatListController.swift:3815` — drop `_asPeer()`: `groupPeer: peer._asPeer() → groupPeer: peer` (`peer` from `engine.data.get(...Item.Peer.Peer(id:))` is EnginePeer). +- `TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift:4006` — boundary lift: `groupPeer: peer → groupPeer: EnginePeer(peer)` (`peer` from `renderedPeer?.peer` is Postbox Peer; needed because `is TelegramGroup || is TelegramChannel` pre-check still runs against the Peer-typed local). + +**Net:** 3 drops (2 `_asPeer()` at PIS:4606 + CLC:3815, 1 redundant `EnginePeer()` wrap at PAM:210), 1 boundary lift (CCLDN:4006), 2 case-let conversions in PAM body. Net internal-bridge progress: **−2**. + +**Iter-2 lesson — pre-flight scope must include "internal wraps inside the migrated function's body."** Pre-flight grep covered: +- `(...:` call sites ✓ +- ` as? Telegram*` casts ✓ +But missed: +- `EnginePeer()` wraps INSIDE the migrated body — once the parameter type changes, those become invalid. + +Adding `\b` (the parameter name) to the pre-flight grep would have caught the PAM:210 site. Cheap rule going forward: grep the body for `EnginePeer\(\)` before declaring inventory complete. + +**Lessons:** + +- **Cross-module migration with protocol method = uniform 2-iter target unless body is small.** `AccountContext` protocol method touches 3 callers + 1 impl + 1 forwarding free function with internal body. Each axis can hide one missed reference. Wave 58 hit one such miss (PAM:210); this is typical. Budget 2 iters for protocol-method migrations even when all axes look complete in inventory. +- **Boundary-lift planning is correct when the call-site's source is foundational.** CCLDN:4006 boundary lift is permanent until `RenderedPeer.peer` migrates. Adding it now is the right shape — defers the foundational migration without inflating the wave to that scope. +- **Pure cross-module migrations don't always cascade-recompile through TelegramCore.** Wave 58 touched TelegramUI consumers + AccountContext protocol but NOT TelegramCore; iter-2 rebuild was 29.9s (typical). Compare to wave 57's 237.9s when TelegramCore was touched. + +--- + +## Wave 59 outcome (2026-04-25) + +`_asPeer() as? TelegramX` micro-cluster migration. 4 files / 7 edits / **first-pass-clean** Bazel 25.833s. Commit `6ca4058ae8`. + +**Edits:** + +- `PeerInfoHeaderEditingContentNode.swift:115` — `(peer?._asPeer() as? TelegramUser)?.lastName ?? ""` → `if case let .user(user) = peer { updateText = user.lastName ?? "" } else { updateText = "" }` (preserves `?? ""` fallback semantics on cast-failure path). +- `StarsTransactionsScreen.swift:1288` — `if let channel = subscription.peer._asPeer() as? TelegramChannel { ... }` → `if case let .channel(channel) = subscription.peer { ... }`. +- `StarsTransactionScreen.swift:280` — `if let creationDate = (subscription.peer._asPeer() as? TelegramChannel)?.creationDate, creationDate > 0 {` → `if case let .channel(channel) = subscription.peer, channel.creationDate > 0 { additionalDate = channel.creationDate ... }`. (TelegramChannel.creationDate is non-optional Int32.) +- `ChatListSearchContainerNode.swift:927` — `if let user = message.author?._asPeer() as? TelegramUser { ... }` → `if case let .user(user) = message.author { ... }` (message.author is EnginePeer?). + +**Net:** 4 internal `_asPeer() as? TelegramX` cast bridges eliminated; 0 adds. PIHEC site adds explicit `else { updateText = "" }` to preserve original cast-failure fallback. + +**Lessons:** + +- **`_asPeer()` is a tag for "this value is EnginePeer" by construction.** `_asPeer()` only exists on `EnginePeer`; calling it on any other type (including `Peer` protocol) doesn't compile. So every consumer-side `X._asPeer()` is provably an EnginePeer source — no need to verify per-site. This invariant makes bulk `_asPeer()` cluster migrations safe. +- **Preserve cast-failure-path semantics when converting `as?` clusters.** Original `(X as? TelegramUser)?.lastName ?? ""` returns `""` if X is nil OR not TelegramUser OR lastName is nil. Naive `if case let .user(user) = X { updateText = user.lastName ?? "" }` only sets updateText for the .user case, leaving it nil for other cases. Add explicit `else` branch when the original `??` fallback is load-bearing. + +--- + +## Wave 60 outcome (2026-04-25) + +Add `PeerReference.init?(_ peer: EnginePeer)` convenience init in TelegramCore + drop **49** consumer-side `PeerReference(X._asPeer())` bridges across 29 files / 53 insertions / 49 deletions. **First-pass-clean** Bazel 238.803s (cascade compile). Commit `b6cc2bfbd1`. + +**TelegramCore extension addition:** + +`submodules/TelegramCore/Sources/ApiUtils/ApiUtils.swift` — appended `init?(_ peer: EnginePeer) { self.init(peer._asPeer()) }` to the existing `public extension PeerReference` block. + +**Consumer drops:** 49 sites where `PeerReference(X._asPeer())` becomes `PeerReference(X)`. 12 distinct expression patterns: `$0`, `author`, `bot.peer`, `component.peer`, `component.slice.effectivePeer`, `component.slice.peer`, `item.peer`, `participantPeer`, `peer`, `peerValue`, `primary.1`, `self.peer`. All EnginePeer-typed by the `_asPeer()` invariant. + +**Execution method:** Python regex replacement (`re.sub` with pattern `PeerReference\(([^)(]+)\._asPeer\(\)\)` → `PeerReference(\1)`). Non-`)`-non-`(` capture group ensures the match is the smallest expression before the trailing `._asPeer())`. Tested against all 12 distinct expression patterns including chained property accesses and closure-arg captures. + +**Net:** 49 internal `_asPeer()` bridge drops; 0 adds. Largest single-wave consumer-drop count to date. + +**Lessons:** + +- **The wave-57 forwarding-extension pattern scales to bulk init/method addition.** Convenience init/method overload in TelegramCore (1 line) drops N consumer bridges (49 in this case). Cost: 1 line + bulk consumer migration. Benefit: O(N) bridges dropped. Threshold for triggering this shape is ≥5 consumer sites (anything fewer is per-site case-by-case). +- **Python regex with `[^)(]+` capture is the safe automation primitive for migrating `Wrapper(X._asPeer())` patterns.** Greedy `.*` captures cause over-match. Non-greedy `.*?` is fragile in BSD sed. The `[^)(]+` capture explicitly avoids both `(` and `)` inside the captured expression, which works for chained property accesses (`a.b.c`), closure args (`$0`), and tuple-indexed accesses (`primary.1`). 49 sites all replaced safely. +- **The `_asPeer()` invariant guarantees migration safety.** Because `_asPeer()` only compiles on EnginePeer, no per-site type verification is needed for `Wrapper(X._asPeer()) → Wrapper(X)` migrations once the EnginePeer-accepting overload is added. + +--- + +## Wave 61 outcome (2026-04-25) + +Drop 6 `_asPeer().X` consumer-side bridges where X is already on the EnginePeer forwarding extension. 3 files / 6 edits / **first-pass-clean** Bazel 19.351s. Commit `5d497cc5e9`. + +**Edits:** + +- `ChannelVisibilityController.swift:1478` — `peer?._asPeer().usernames` → `peer?.usernames`. +- `PeerInfoCoverComponent.swift:74` — `peer._asPeer().profileColor` → `peer.profileColor`. +- `PeerInfoCoverComponent.swift:82` — `peer._asPeer().nameColor` → `peer.nameColor`. +- `StoryItemSetContainerComponent.swift:6700/6956/7274` — `component.slice.effectivePeer._asPeer().usernames` → `component.slice.effectivePeer.usernames` (replace_all=true; 3 sites). + +**Net:** 6 internal `_asPeer()` bridge drops; 0 adds. All 6 sites used properties (`usernames`, `profileColor`, `nameColor`) already present on `public extension EnginePeer` block in TelegramCore — no new TelegramCore touch needed (no cascade compile). + +**Skipped:** `peer._asPeer().indexName.indexTokens` at `ChannelDiscussionGroupSearchContainerNode.swift:157` — `EnginePeer.indexName` returns wrapper enum `EnginePeer.IndexName` without `.indexTokens`. Needs a separate IndexName extension or consumer refactor. Defer. + +--- + +## Wave 62 outcome (2026-04-25) + +Add 4 EnginePeer forwarding entries (isMonoForum, associatedPeerId, hasCustomNameColor, hasSensitiveContent) + drop 5 consumer-side `_asPeer().X` bridges. 6 files / 9 edits / **first-pass-clean** Bazel 237.769s (cascade compile). Commit `cdce0dba01`. + +**TelegramCore extension additions** (Peer.swift, 4 entries — 16 lines): + +```swift +var isMonoForum: Bool { + return self._asPeer().isMonoForum +} +var associatedPeerId: Id? { + return self._asPeer().associatedPeerId +} +var hasCustomNameColor: Bool { + return self._asPeer().hasCustomNameColor +} +func hasSensitiveContent(platform: String) -> Bool { + return self._asPeer().hasSensitiveContent(platform: platform) +} +``` + +All four forwarding implementations live in `submodules/TelegramCore/Sources/Utils/PeerUtils.swift` on the `extension Peer { ... }` block. The `Id` typealias on EnginePeer is `PeerId`, so `associatedPeerId: Id?` is type-equivalent to the underlying `Peer.associatedPeerId: PeerId?`. + +**Consumer drops** (5 sites): + +- `ThemeSettingsController.swift:428` — `accountPeer._asPeer().hasCustomNameColor` → `accountPeer.hasCustomNameColor`. +- `AgeVerificationScreen.swift:31` — `peer._asPeer().hasSensitiveContent(platform: "ios")` → `peer.hasSensitiveContent(platform: "ios")`. +- `TextProcessingScreen.swift:1281` — `peer._asPeer().isMonoForum` → `peer.isMonoForum`. +- `ShareSearchContainerNode.swift:478` — `mainPeer._asPeer().associatedPeerId` → `mainPeer.associatedPeerId`. +- `ChatListSearchListPaneNode.swift:153` — `maybeChatPeer._asPeer().associatedPeerId` → `maybeChatPeer.associatedPeerId`. + +**Net:** 5 internal bridge drops; 0 adds. + +**Lessons:** + +- **Bundle multi-property forwarding additions in a single TelegramCore touch.** When 4 properties each unblock 1-2 consumer drops, bundling the additions into one wave amortizes the cascade-recompile cost (~210s) across 5 drops instead of paying 4× across 4 separate waves. + +--- + +## Wave 63 outcome (2026-04-25) + +`resolvedAreStoriesMuted(peer:)` Peer → EnginePeer cross-module function-signature migration. 5 files / 11 edits / **first-pass-clean** Bazel 239.818s (cascade compile). Commit `499edc0ddb`. + +**TelegramCore signature change** (`ChangePeerNotificationSettings.swift`): +- :65 — `peer: Peer → peer: EnginePeer`. Body uses only `peer.id` (works on EnginePeer). +- :117 — internal call site boundary lift: `peer: peer → peer: EnginePeer(peer)` (peer source is `transaction.getPeer(peerId)` returning Postbox Peer). + +**Consumer drops** (9 sites, 4 files): +- `ContactContextMenus.swift:50` — drop `_asPeer()`. +- `ChatListController.swift:3317` — drop `_asPeer()`. +- `StoryItemSetContainerComponent.swift:7224` — drop `_asPeer()` on `component.slice.effectivePeer`. +- `StoryChatContent.swift` — 6 sites (lines 210, 212, 1287, 1599, 2490, 2492); pattern `peer: peer._asPeer(),` → `peer: peer,` via `replace_all=true`. + +**Net:** 9 drops, 1 boundary lift = **−8 internal bridges**. + +**Lessons:** + +- **Cross-module function-signature migration is a uniform 1-iter target when (a) the function body uses only EnginePeer-compatible properties and (b) all call sites have EnginePeer-typed sources via `_asPeer()`.** Pre-flight grep `.*_asPeer` enumerates all consumer-side bridges; each drop is mechanical. Internal call sites in TelegramCore that pass Postbox Peer-typed locals get a boundary lift. +- **`replace_all=true` shines when the same parameter pattern repeats in a single file.** StoryChatContent had 6 identical `peer: peer._asPeer(),` patterns — one Edit call with `replace_all=true` replaced all of them. Saves 5 round-trips. + +--- + +## Wave 64 outcome (2026-04-25) + +`RenderedPeer.convenience init(peer: EnginePeer)` in TelegramCore + 5 consumer drops. 3 files / 6 edits / **first-pass-clean** Bazel 237.725s (cascade compile). Commit `109c2fe172`. Wave-shape: foundational TelegramCore extension addition (wave-57 pattern). Original `init(peer: Peer)` retained as designated init; Swift overload resolution selects the EnginePeer init only when the argument is EnginePeer-typed. Sites: PeerInfoSettingsItems:131 + ChatListSearchListPaneNode:4173/4213/4336/4371. + +## Wave 65 outcome (2026-04-25) + +`MergedAvatarsNode.update(peers: [Peer]) → [EnginePeer]` + private `PeerAvatarReference.init(peer:)` cascade. 6 files / 9 edits / **3 iter**. Commit `37c680c86c`. Iter-2 missed `groupsInCommon: [Peer]` field at ChatUserInfoItem; iter-3 missed `recentVoterPeers: [Peer]` and `avatarPeers: [Peer]` at ChatMessagePollBubbleContentNode. Lesson: **migrating a public function's array-of-Peer parameter forces a transitive inventory of *all* `[Peer]` field declarations across consumer modules**. Pre-flight grep should include `\[Peer\]\s*=\s*\[\]` and similar local/field decls. + +## Wave 66 outcome (2026-04-25) + +`VoiceChatJoinScreen.setPeer` + `VoiceChatPreviewContentNode.init` chain Peer→EnginePeer. Single file / 5 edits / first-pass-clean. Commit `148aa53f3f`. Net **−3** internal bridges. Wave-55 single-file pure-drop pattern. + +## Wave 67 outcome (2026-04-25) + +`_internal_storedMessageFromSearchPeer` signature Peer→EnginePeer + return type Signal→Signal. 3 files / 5 edits / first-pass-clean Bazel 226.884s. Commit `3732fb66b6`. Drops `peer._asPeer()` and `|> map { EnginePeer(result) }` in `ensurePeerIsLocallyAvailable`. Net **−1** with 2 internal boundary lifts inside TelegramCore body. + +## Wave 68 outcome (2026-04-25) + +`SelectivePrivacyPeer.convenience init(peer: EnginePeer, participantCount:)` + 3 consumer drops. 4 files / 4 edits / first-pass-clean (initial 221s after TelegramCore touch + 17s consumer-only rebuild). Commit `73811a4e5d`. Original `init(peer: Peer)` and stored `peer: Peer` field unchanged — full migration of the field deferred (69 references repo-wide). + +## Wave 69 outcome (2026-04-25) + +`_internal_storedMessageFromSearchPeers` (plural) Peer→EnginePeer. Closes wave-67 sibling. 2 files / 3 edits / first-pass-clean Bazel 225.143s. Commit `42252eb9fd`. Drops `peers.map { $0._asPeer() }` in `ensurePeersAreLocallyAvailable`. + +## Wave 70 outcome (2026-04-25) + +`StatsMessageItem.peer` + `ChannelStatsController .post` enum-case payload + `MessageStatsController` local var Peer→EnginePeer cascade. 3 files / 9 edits / **3 iter**. Commit `f14dfe2273`. Iter-2 missed `entries.append(.post)` building sites at ChannelStatsController:1429/1433. Iter-3 missed `arePeersEqual(lhsPeer, rhsPeer)` Equatable comparison at L639 — replaced with `lhsPeer == rhsPeer` since EnginePeer is Equatable. **Pre-flight rule: when migrating an enum-case payload from Peer to EnginePeer, grep the same enum's `==` Equatable conformance for `arePeersEqual` calls.** Net **−5** internal bridges. + +## Wave 71 outcome (2026-04-25) + +`peerInfoControllerImpl` signature Peer→EnginePeer + drop `_asPeer()` shadow at SAC:1938. 1 file / 6 edits / first-pass-clean Bazel 24.582s. Commit `1650bc1521`. Wave-shape: single-file private-function with shadow-assignment pattern (`let peer = peer._asPeer()` shadowing the EnginePeer parameter to a Peer local). Mechanical conversion of body's 4 `as?`/`is` checks. Cheap rebuild because no public API change. + +## Wave 72 outcome (2026-04-25) + +`canSendMessagesToPeer` body cleanup: drop `_asPeer()` shadow + restructure `as?` casts as exhaustive switch on EnginePeer cases. 1 file / 1 edit / first-pass-clean Bazel 25.792s. Commit `98e7158b7a`. Wave-shape: TelegramCore-internal body cleanup. Public API was already EnginePeer (wave 38) — this wave only refactors the implementation. Cheap rebuild despite TelegramCore touch. + +## Wave 73 outcome (2026-04-25) + +`ChatQrCodeScreenImpl.Subject.peer` enum-payload Peer→EnginePeer + `QrContentNode.peer` field migration + drop `_asPeer()` shadow at SAC:2731. 2 files / 8 edits / **2 iter**. Commit `0faf4a0336`. Iter-2 missed two additional `peer as? TelegramX` casts at QrContentNode body L1742/L1744; also fixed an over-aggressive `replace_all=true` that caught a different `peer: EnginePeer(peer)` site at MessageContentNode L2184 where the local `peer` is Postbox-typed (boundary lift restored). + +**Lesson reinforced: `replace_all=true` on a parametric wrapping pattern (`peer: EnginePeer(peer)`) must verify ALL matches share the same scope/source.** When the same call appears in multiple scopes with different local-variable types, `replace_all` propagates the wrong intent. Mitigation: grep first to verify unique-scope assumption, or fall back to per-site Edit when unsure. + +--- + ## Modules currently free of `import Postbox` (running tally) Consumer modules that no longer import Postbox, across all waves and standalone commits: diff --git a/docs/superpowers/specs/2026-04-25-peerinfo-enclosingpeer-engine-peer.md b/docs/superpowers/specs/2026-04-25-peerinfo-enclosingpeer-engine-peer.md new file mode 100644 index 0000000000..1d9a3fe296 --- /dev/null +++ b/docs/superpowers/specs/2026-04-25-peerinfo-enclosingpeer-engine-peer.md @@ -0,0 +1,127 @@ +# Wave 50 — `enclosingPeer` Peer? → EnginePeer? + +**Date:** 2026-04-25 +**Pattern:** struct-field + stored-form `Peer?` → `EnginePeer?` (wave-47/48 shape). +**Module:** `submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/` only — no public-API leaks. + +## Goal + +Migrate the PeerInfo members chain's `enclosingPeer` field from raw Postbox `Peer?` to `EnginePeer?`. Drops 2 `_asPeer()` demotions, 1 `EnginePeer(...)` wrap, 1 `flatMap(EnginePeer.init)` simplification, and 1 PSPB boundary `_asPeer()` lift. Closes the wave-48-pattern internal-demotion-and-external-re-promotion ratchet at PIMP:354–363 (engine.data subscription returns `EnginePeer?`, currently demoted to `Peer?` at the storage boundary). + +## Type changes + +| File | Site | Before | After | +|---|---|---|---| +| `PeerInfoScreenMemberItem.swift:23` | stored `let enclosingPeer` | `Peer?` | `EnginePeer?` | +| `PeerInfoScreenMemberItem.swift:34` | init param | `Peer?` | `EnginePeer?` | +| `PeerInfoMembersPane.swift:92` | `func item(... enclosingPeer:)` | `Peer` | `EnginePeer` | +| `PeerInfoMembersPane.swift:271` | `func preparedTransition(... enclosingPeer:)` | `Peer` | `EnginePeer` | +| `PeerInfoMembersPane.swift:293` | `private var enclosingPeer` | `Peer?` | `EnginePeer?` | +| `PeerInfoMembersPane.swift:442` | `func updateState(enclosingPeer:)` | `Peer` | `EnginePeer` | + +`PeerInfoScreenMemberItem` and `PeerInfoMembersPaneNode` are local to the module — no cross-module signature ripple. + +## Edit patterns + +### A. Conditional cast → case-let (wave-41/45 idiom) + +| File:Line | Before | After | +|---|---|---| +| PSMI:152 | `if let channel = item.enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank)` | `if case let .channel(channel) = item.enclosingPeer, channel.hasPermission(.editRank)` | +| PSMI:154 | `else if let group = item.enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank)` | `else if case let .legacyGroup(group) = item.enclosingPeer, !group.hasBannedPermission(.banEditRank)` | +| PIMP:113 | `if let channel = enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank)` | `if case let .channel(channel) = enclosingPeer, channel.hasPermission(.editRank)` | +| PIMP:115 | `else if let group = enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank)` | `else if case let .legacyGroup(group) = enclosingPeer, !group.hasBannedPermission(.banEditRank)` | + +The `case let` pattern binds `channel: TelegramChannel` / `group: TelegramGroup` directly — `.hasPermission(.editRank)` and `.hasBannedPermission(.banEditRank)` are class methods on the bound concrete types. No `_asPeer()` bridge needed. + +### B. `is`-check → `case` (wave-41 always-false-warning fix) + +| File:Line | Before | After | +|---|---|---| +| PSMI:181 | `if actions.contains(.promote) && item.enclosingPeer is TelegramChannel` | `if actions.contains(.promote), case .channel = item.enclosingPeer` | +| PSMI:187 | `if item.enclosingPeer is TelegramChannel` | `if case .channel = item.enclosingPeer` | +| PIMP:142 | `if actions.contains(.promote) && enclosingPeer is TelegramChannel` | `if actions.contains(.promote), case .channel = enclosingPeer` | +| PIMP:148 | `if enclosingPeer is TelegramChannel` | `if case .channel = enclosingPeer` | + +PIMP:113/115/142/148 are inside `func item(... enclosingPeer: EnginePeer ...)`, so `enclosingPeer` is non-optional inside that body; PSMI sites are against `item.enclosingPeer: EnginePeer?`. `case let .channel(channel)` and `case .channel` both compile cleanly against optional and non-optional EnginePeer. + +### C. Drop wraps / unwraps + +| File:Line | Before | After | +|---|---|---| +| PSMI:178 | `peer: item.enclosingPeer.flatMap(EnginePeer.init)` | `peer: item.enclosingPeer` | +| PIMP:139 | `peer: EnginePeer(enclosingPeer)` | `peer: enclosingPeer` | +| PIMP:361 | `strongSelf.enclosingPeer = enclosingPeer._asPeer()` | `strongSelf.enclosingPeer = enclosingPeer` | +| PIMP:363 | `updateState(enclosingPeer: enclosingPeer._asPeer(), state: state, presentationData: presentationData)` | `updateState(enclosingPeer: enclosingPeer, state: state, presentationData: presentationData)` | +| PSPB:852 | `enclosingPeer: peer._asPeer()` | `enclosingPeer: peer` | + +### D. No-op call sites (type flows through transparently) + +- `PeerInfoSettingsItems.swift:132` — `enclosingPeer: nil` (nil literal works for any optional) +- `PeerInfoMembersPane.swift:275/276` — pass-through `enclosingPeer: enclosingPeer` +- `PeerInfoMembersPane.swift:437/438` — `if let enclosingPeer = self.enclosingPeer ... self.updateState(enclosingPeer: enclosingPeer, ...)` (both stored-form and `updateState` param shift to EnginePeer; type carries through) +- `PeerInfoMembersPane.swift:451` — pass-through +- `PeerInfoMembersPane.swift:485` — `self.enclosingPeer = enclosingPeer` (param and stored-form both EnginePeer) +- `PeerInfoScreenOpenMember.swift` — uses `self.data?.peer` (already `EnginePeer?` post-wave-42), unrelated to this migration + +**Total edits:** 19 across 3 files (PSMI, PIMP, PSPB) — 6 type-change edits in the table at the top of this spec + 4 (Pattern A) + 4 (Pattern B) + 5 (Pattern C). + +## Risk register + +| Risk | Mitigation | +|---|---| +| `case .channel = item.enclosingPeer` against `EnginePeer?` semantics | Wave-45 lesson confirms `case let .x(y) = peer` compiles cleanly against `EnginePeer?`. Matches `.some(.channel)`, rejects `nil` and other cases — equivalent to `is TelegramChannel` semantics. | +| `if actions.contains(.promote), case .channel = ...` mixed boolean + pattern condition | Standard Swift if-case syntax (introduced in wave 41 idiom for this codebase). | +| Hidden Peer-only property access on bare `enclosingPeer` | Pre-flight grep complete: only access patterns are `.id` (EnginePeer has it), and cast-bound `channel.hasPermission` / `group.hasBannedPermission`. No `_asPeer()` bridges expected. | +| Closure capture aliases (wave-47 lesson) | Pre-flight grep covered `strongSelf.enclosingPeer` (PIMP:361) and `self.enclosingPeer` (PIMP:437/485). | +| `enclosingPeer: nil` literal at PSI:132 | `nil` is valid for any optional — no edit. | +| `availableActionsForMemberOfPeer` signature compatibility | Confirmed `EnginePeer?` at `PeerInfoData.swift:2314`. Both PSMI:178 and PIMP:139 are pure simplifications. | +| Always-false `is` check warning under `-warnings-as-errors` | Wave-41 lesson — handled by Pattern B. | + +## Wave shape + +**Classification:** cross-file private struct-field migration with stored-form ratchet (wave-47 taxonomy: "cross-file private"). +**Iteration budget:** 1–2 (target first-pass-clean per wave 48/49 streak). +**Subagent dispatch:** not needed — 17 edits / 3 files is single-implementer scope. + +## Verification + +### Build + +```sh +source ~/.zshrc 2>/dev/null; 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 +``` + +### Post-edit residue grep (expect empty) + +```sh +grep -rnE "enclosingPeer\._asPeer|EnginePeer\(enclosingPeer\)|enclosingPeer\.flatMap\(EnginePeer" \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ + +grep -rnE "enclosingPeer.*as\? TelegramChannel|enclosingPeer.*as\? TelegramGroup|enclosingPeer is TelegramChannel" \ + submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ +``` + +## Net delta projection + +- **Internal bridges:** −5 (2× `_asPeer()` at PIMP:361/363, 1× `EnginePeer(...)` at PIMP:139, 1× `flatMap(EnginePeer.init)` at PSMI:178, 1× boundary `_asPeer()` at PSPB:852). +- **Boundary lifts:** 0 net new — the source pipeline (engine.data subscription at PIMP:354) already yields `EnginePeer?`. Migration just removes the demote-then-promote dance. +- **ADD wraps:** 0 expected (no Peer-only property accesses on bare `enclosingPeer`). + +## Out of scope + +- `PeerInfoScreenData.chatPeer: Peer?` — large cascade (PSPB `as? TelegramX` × 5, ClearPeerHistory cascade, openClearHistory wraps × 4, PSOC × 2). Memory's wave-50 candidate Option 3, deferred for a multi-iteration wave. +- `PeerInfoGroupsInCommonPaneNode.PeerEntry.peer: Peer` — separate single-file migration, not bundled (wave-49 source-of-truth-coherence rule: unrelated chains stay in their own waves). Candidate for wave 51. +- `RenderedPeer → EngineRenderedPeer` foundational refactor — dedicated session. + +## Memory file update + +After landing, update `project_postbox_refactor_next_wave.md`: +- Move wave 50 outcome into the recent-waves list. +- Promote wave 51 candidate (`PeerInfoGroupsInCommonPaneNode.PeerEntry.peer` likely; otherwise re-scan the module with the standard grep). diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 4298a3f67a..4f79cd0036 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -111,18 +111,18 @@ public enum TextLinkItem: Equatable { public final class AccountWithInfo: Equatable { public let account: Account - public let peer: Peer - - public init(account: Account, peer: Peer) { + public let peer: EnginePeer + + public init(account: Account, peer: EnginePeer) { self.account = account self.peer = peer } - + public static func ==(lhs: AccountWithInfo, rhs: AccountWithInfo) -> Bool { if lhs.account !== rhs.account { return false } - if !arePeersEqual(lhs.peer, rhs.peer) { + if lhs.peer != rhs.peer { return false } return true @@ -1453,7 +1453,7 @@ public protocol SharedAccountContext: AnyObject { completion: @escaping (UIImage?) -> Void, completedWithUploadingImage: @escaping (UIImage, Signal) -> UIView? ) - func openAddPeerMembers(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) + func openAddPeerMembers(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: EnginePeer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) func makeInstantPageController(context: AccountContext, message: Message, sourcePeerType: MediaAutoDownloadPeerType?) -> ViewController? func makeInstantPageController(context: AccountContext, webPage: TelegramMediaWebpage, anchor: String?, sourceLocation: InstantPageSourceLocation) -> ViewController func openChatWallpaper(context: AccountContext, message: Message, present: @escaping (ViewController, Any?) -> Void) diff --git a/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift b/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift index 598e68063a..c00eb947a1 100644 --- a/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift +++ b/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift @@ -91,7 +91,7 @@ private final class ContentNode: ASDisplayNode { self.addSubnode(self.clippedNode) if let peer = peer { - if let representation = peer.smallProfileImage, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: representation, displayDimensions: size, synchronousLoad: synchronousLoad) { + if let representation = peer.smallProfileImage, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: representation, displayDimensions: size, synchronousLoad: synchronousLoad) { let image = generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(UIColor.lightGray.cgColor) @@ -328,7 +328,7 @@ public final class AnimatedAvatarSetView: UIView { self.addSubview(self.clippedView) if let peer = peer { - if let representation = peer.smallProfileImage, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: representation, displayDimensions: size, synchronousLoad: synchronousLoad) { + if let representation = peer.smallProfileImage, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: representation, displayDimensions: size, synchronousLoad: synchronousLoad) { let image = generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(UIColor.lightGray.cgColor) diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 1f015d8259..ac85742469 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -1718,14 +1718,14 @@ public class AttachmentController: ViewController, MinimizableController { |> deliverOnMainQueue).startStandalone(next: { bots in for bot in bots { for (name, file) in bot.icons { - if [.iOSAnimated, .placeholder].contains(name), let peer = PeerReference(bot.peer._asPeer()) { + if [.iOSAnimated, .placeholder].contains(name), let peer = PeerReference(bot.peer) { if case .placeholder = name { let path = context.account.postbox.mediaBox.cachedRepresentationCompletePath(file.resource.id, representation: CachedPreparedSvgRepresentation()) if !FileManager.default.fileExists(atPath: path) { let accountFullSizeData = Signal<(Data?, Bool), NoError> { subscriber in let accountResource = context.account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedPreparedSvgRepresentation(), complete: false, fetch: true) - let fetchedFullSize = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: MediaResourceUserContentType(file: file), reference: .media(media: .attachBot(peer: peer, media: file), resource: file.resource)) + let fetchedFullSize = context.engine.resources.fetch(reference: .media(media: .attachBot(peer: peer, media: file), resource: file.resource), userLocation: .other, userContentType: MediaResourceUserContentType(file: file)) let fetchedFullSizeDisposable = fetchedFullSize.start() let fullSizeDisposable = accountResource.start() diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 4b2d4a776a..7c4e1b3f04 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -302,7 +302,7 @@ private final class AttachButtonComponent: CombinedComponent { ) } else { var fileReference: FileMediaReference? - if let peer = botPeer.flatMap({ PeerReference($0._asPeer())}), let imageFile = imageFile { + if let peer = botPeer.flatMap({ PeerReference($0)}), let imageFile = imageFile { fileReference = .attachBot(peer: peer, media: imageFile) } @@ -2065,7 +2065,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecog if case let .app(bot) = type { for (name, file) in bot.icons { if [.default, .iOSAnimated, .iOSSettingsStatic, .placeholder].contains(name) { - if self.iconDisposables[file.fileId] == nil, let peer = PeerReference(bot.peer._asPeer()) { + if self.iconDisposables[file.fileId] == nil, let peer = PeerReference(bot.peer) { if case .placeholder = name { let account = self.context.account let path = account.postbox.mediaBox.cachedRepresentationCompletePath(file.resource.id, representation: CachedPreparedSvgRepresentation()) diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 115c8c0149..bbecec9430 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -643,7 +643,7 @@ public final class AvatarNode: ASDisplayNode { let parameters: AvatarNodeParameters - if let peer = peer, let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded, cutoutRect: cutoutRect) { + if let peer = peer, let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded, cutoutRect: cutoutRect) { self.contents = nil self.displaySuspended = true self.imageReady.set(self.imageNode.contentReady) @@ -763,7 +763,7 @@ public final class AvatarNode: ASDisplayNode { self.imageNode.view.mask = nil } - if let imageCache = genericContext.imageCache as? DirectMediaImageCache, let peer, let smallProfileImage = peer.smallProfileImage, let peerReference = PeerReference(peer._asPeer()) { + if let imageCache = genericContext.imageCache as? DirectMediaImageCache, let peer, let smallProfileImage = peer.smallProfileImage, let peerReference = PeerReference(peer) { if let result = imageCache.getAvatarImage(peer: peerReference, resource: MediaResourceReference.avatar(peer: peerReference, resource: smallProfileImage.resource), immediateThumbnail: peer.profileImageRepresentations.first?.immediateThumbnailData, size: Int(displayDimensions.width * UIScreenScale), synchronous: synchronousLoad) { if let image = result.image { self.imageNode.contents = image.cgImage @@ -852,7 +852,7 @@ public final class AvatarNode: ASDisplayNode { let account = account ?? genericContext.account - if let peer = peer, let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded, cutoutRect: cutoutRect) { + if let peer = peer, let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded, cutoutRect: cutoutRect) { self.contents = nil self.displaySuspended = true self.imageReady.set(self.imageNode.contentReady) diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 17d3374616..61b7742175 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -126,8 +126,8 @@ public func peerAvatarCompleteImage(postbox: Postbox, network: Network, peer: En thumbnailRepresentation = peer.profileImageRepresentations.first } - if let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: thumbnailRepresentation, displayDimensions: size, clipStyle: clipStyle, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { - if fullSize, let fullSizeSignal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) { + if let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer), authorOfMessage: nil, representation: thumbnailRepresentation, displayDimensions: size, clipStyle: clipStyle, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { + if fullSize, let fullSizeSignal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) { iconSignal = combineLatest(.single(nil) |> then(signal), .single(nil) |> then(fullSizeSignal)) |> mapToSignal { thumbnailImage, fullSizeImage -> Signal in if let fullSizeImage = fullSizeImage { diff --git a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift index 9618b3cd00..c541eda360 100644 --- a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift +++ b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift @@ -206,7 +206,7 @@ public final class AvatarVideoNode: ASDisplayNode { self.internalSize = size if let markup = photo.emojiMarkup { self.update(markup: markup, size: size, useAnimationNode: false) - } else if let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) { + } else if let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer) { self.backgroundNode.image = nil let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value() @@ -217,7 +217,7 @@ public final class AvatarVideoNode: ASDisplayNode { self.videoContent = videoContent self.videoFileDisposable?.dispose() - self.videoFileDisposable = fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .avatar, reference: videoFileReference.resourceReference(videoFileReference.media.resource)).startStrict() + self.videoFileDisposable = self.context.engine.resources.fetch(reference: videoFileReference.resourceReference(videoFileReference.media.resource), userLocation: .peer(peer.id), userContentType: .avatar).startStrict() } } } diff --git a/submodules/BrowserUI/Sources/BrowserMarkdown.swift b/submodules/BrowserUI/Sources/BrowserMarkdown.swift index debef1e807..2ca7c693e7 100644 --- a/submodules/BrowserUI/Sources/BrowserMarkdown.swift +++ b/submodules/BrowserUI/Sources/BrowserMarkdown.swift @@ -140,7 +140,7 @@ private final class MarkdownConversionContext { ) case let .data(data, dimensions): let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: Int64(data.count), isSecretRelated: false) - self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) + self.context.engine.resources.storeResourceData(id: EngineMediaResource.Id(resource.id), data: data) let mediaId = self.nextMediaId(namespace: Namespaces.Media.LocalImage) self.media[mediaId] = TelegramMediaImage( @@ -185,7 +185,7 @@ func markdownWebpage(context: AccountContext, file: FileMediaReference) -> (webP guard #available(iOS 15.0, *) else { return nil } - guard let path = context.account.postbox.mediaBox.completedResourcePath(file.media.resource) else { + guard let path = context.engine.resources.completedResourcePath(id: EngineMediaResource.Id(file.media.resource.id)) else { return nil } let fileURL = URL(fileURLWithPath: path) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index dbdfbbde41..62a2b18d10 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -472,7 +472,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController var entry: FetchManagerEntrySummary var isRemoved: Bool = false var statusDisposable: Disposable? - var status: MediaResourceStatus? + var status: EngineMediaResource.FetchStatus? init(entry: FetchManagerEntrySummary) { self.entry = entry @@ -522,7 +522,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } if context.statusDisposable == nil { - context.statusDisposable = (engine.account.postbox.mediaBox.resourceStatus(context.entry.resourceReference.resource) + context.statusDisposable = (engine.resources.status(resource: EngineMediaResource(context.entry.resourceReference.resource)) |> deliverOn(self.queue)).startStrict(next: { [weak self, weak context] status in guard let strongSelf = self, let context = context else { return @@ -3314,7 +3314,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) }))) - let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: topSearchPeers) + let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: topSearchPeers) items.append(.action(ContextMenuActionItem(text: isMuted ? self.presentationData.strings.StoryFeed_ContextNotifyOn : self.presentationData.strings.StoryFeed_ContextNotifyOff, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in @@ -3812,7 +3812,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } let selectAddMemberDisposable = MetaDisposable() let addMemberDisposable = MetaDisposable() - context.sharedContext.openAddPeerMembers(context: context, updatedPresentationData: nil, parentController: sourceController, groupPeer: peer._asPeer(), selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable) + context.sharedContext.openAddPeerMembers(context: context, updatedPresentationData: nil, parentController: sourceController, groupPeer: peer, selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable) }) }))) } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 1e4769d81f..9263c1b5ca 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -924,7 +924,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } var type: PeerType = .group for message in messages { - if let user = message.author?._asPeer() as? TelegramUser { + if case let .user(user) = message.author { if user.botInfo != nil && !user.id.isVerificationCodes { type = .bot } else { diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index d22e9e99ac..d702a7d420 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -150,7 +150,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { let primaryPeer: EnginePeer var chatPeer: EnginePeer? let maybeChatPeer = EnginePeer(peer.peer.peers[peer.peer.peerId]!) - if case .secretChat = maybeChatPeer, let associatedPeerId = maybeChatPeer._asPeer().associatedPeerId, let associatedPeer = peer.peer.peers[associatedPeerId] { + if case .secretChat = maybeChatPeer, let associatedPeerId = maybeChatPeer.associatedPeerId, let associatedPeer = peer.peer.peers[associatedPeerId] { primaryPeer = EnginePeer(associatedPeer) chatPeer = maybeChatPeer } else if case .channel = maybeChatPeer, let mainChannel = peer.peer.chatOrMonoforumMainPeer { @@ -4170,7 +4170,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { result.append(.peer( index: result.count, peer: RecentlySearchedPeer( - peer: RenderedPeer(peer: peer._asPeer()), + peer: RenderedPeer(peer: peer), presence: nil, notificationSettings: peerNotificationSettings.flatMap({ $0._asNotificationSettings() }), unreadCount: unreadCount, @@ -4210,7 +4210,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { result.append(.peer( index: result.count, peer: RecentlySearchedPeer( - peer: RenderedPeer(peer: peer._asPeer()), + peer: RenderedPeer(peer: peer), presence: nil, notificationSettings: peerNotificationSettings.flatMap({ $0._asNotificationSettings() }), unreadCount: 0, @@ -4333,7 +4333,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { result.append(.peer( index: result.count, peer: RecentlySearchedPeer( - peer: RenderedPeer(peer: peer._asPeer()), + peer: RenderedPeer(peer: peer), presence: nil, notificationSettings: peerNotificationSettings.flatMap({ $0._asNotificationSettings() }), unreadCount: unreadCount, @@ -4368,7 +4368,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { result.append(.peer( index: result.count, peer: RecentlySearchedPeer( - peer: RenderedPeer(peer: peer._asPeer()), + peer: RenderedPeer(peer: peer), presence: nil, notificationSettings: peerNotificationSettings.flatMap({ $0._asNotificationSettings() }), unreadCount: 0, diff --git a/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift b/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift index 5754fbb833..5d73c83e7c 100644 --- a/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift +++ b/submodules/Components/ReactionImageComponent/Sources/ReactionImageComponent.swift @@ -18,7 +18,7 @@ public let sharedReactionStaticImage = Queue(name: "SharedReactionStaticImage", public func reactionStaticImage(context: AccountContext, animation: TelegramMediaFile, pixelSize: CGSize, queue: Queue) -> Signal { return context.engine.resources.custom(id: "\(animation.resource.id.stringRepresentation):reaction-static-\(pixelSize.width)x\(pixelSize.height)-v10", fetch: EngineMediaResource.Fetch { return Signal { subscriber in - let fetchDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .image, reference: MediaResourceReference.standalone(resource: animation.resource)).start() + let fetchDisposable = context.engine.resources.fetch(reference: MediaResourceReference.standalone(resource: animation.resource), userLocation: .other, userContentType: .image).start() var customColor: UIColor? if animation.isCustomTemplateEmoji { diff --git a/submodules/ContactListUI/Sources/ContactContextMenus.swift b/submodules/ContactListUI/Sources/ContactContextMenus.swift index 7f5e908085..290b996321 100644 --- a/submodules/ContactListUI/Sources/ContactContextMenus.swift +++ b/submodules/ContactListUI/Sources/ContactContextMenus.swift @@ -47,7 +47,7 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con }) }))) - let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: topSearchPeers) + let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: topSearchPeers) items.append(.action(ContextMenuActionItem(text: isMuted ? strings.StoryFeed_ContextNotifyOn : strings.StoryFeed_ContextNotifyOff, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) diff --git a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift index 85c8102112..7d1b52c291 100644 --- a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift @@ -183,7 +183,7 @@ class ChatDocumentGalleryItemNode: ZoomableContentGalleryItemNode, WKNavigationD self.maybeLoadContent() self.setupStatus(context: context, resource: fileReference.media.resource) - self.fetchDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: (self.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, userContentType: .file, reference: fileReference.resourceReference(fileReference.media.resource)).start()) + self.fetchDisposable.set(context.engine.resources.fetch(reference: fileReference.resourceReference(fileReference.media.resource), userLocation: (self.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, userContentType: .file).start()) } } @@ -388,7 +388,7 @@ class ChatDocumentGalleryItemNode: ZoomableContentGalleryItemNode, WKNavigationD case .Fetching: context.engine.resources.cancelInteractiveResourceFetch(id: EngineMediaResource.Id(fileReference.media.resource.id)) case .Remote: - self.fetchDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: (self.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, userContentType: .file, reference: fileReference.resourceReference(fileReference.media.resource)).start()) + self.fetchDisposable.set(context.engine.resources.fetch(reference: fileReference.resourceReference(fileReference.media.resource), userLocation: (self.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, userContentType: .file).start()) default: break } diff --git a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift index faf906a909..67f671959d 100644 --- a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift @@ -322,7 +322,7 @@ class ChatExternalFileGalleryItemNode: GalleryItemNode { case .Fetching: context.engine.resources.cancelInteractiveResourceFetch(id: EngineMediaResource.Id(fileReference.media.resource.id)) case .Remote: - self.fetchDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: (self.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, userContentType: .file, reference: fileReference.resourceReference(fileReference.media.resource)).start()) + self.fetchDisposable.set(context.engine.resources.fetch(reference: fileReference.resourceReference(fileReference.media.resource), userLocation: (self.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, userContentType: .file).start()) default: break } diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 4c8a4db5ec..6e0913d2c5 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -507,7 +507,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { self.zoomableContent = (largestSize.dimensions.cgSize, self.imageNode) - self.fetchDisposable.set(fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: imageReference.resourceReference(largestSize.resource)).start()) + self.fetchDisposable.set(self.context.engine.resources.fetch(reference: imageReference.resourceReference(largestSize.resource), userLocation: userLocation, userContentType: .image).start()) self.setupStatus(resource: largestSize.resource) } else { self._ready.set(.single(Void())) @@ -932,7 +932,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { } self._rightBarButtonItems.set(.single(barButtonItems)) - self.fetchDisposable.set(fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: fileReference.resourceReference(fileReference.media.resource)).start()) + self.fetchDisposable.set(self.context.engine.resources.fetch(reference: fileReference.resourceReference(fileReference.media.resource), userLocation: userLocation, userContentType: .image).start()) } else { let _ = (chatMessageFileDatas(account: context.account, userLocation: userLocation, fileReference: fileReference, progressive: false, fetched: true) |> mapToSignal { value -> Signal in diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index be9aee83dc..8929b51249 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -3,7 +3,6 @@ import CoreLocation import SwiftSignalKit import StoreKit import TelegramCore -import Postbox import TelegramStringFormatting import TelegramUIPreferences import PersistentStringHash @@ -623,9 +622,9 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver { } let id = Int64.random(in: Int64.min ... Int64.max) let fileResource = LocalFileMediaResource(fileId: id, size: Int64(receiptData.count), isSecretRelated: false) - engine.account.postbox.mediaBox.storeResourceData(fileResource.id, data: receiptData) + engine.resources.storeResourceData(id: EngineMediaResource.Id(fileResource.id), data: receiptData) - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(receiptData.count), attributes: [.FileName(fileName: "Receipt.dat")], alternativeRepresentations: []) + let file = TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(receiptData.count), attributes: [.FileName(fileName: "Receipt.dat")], alternativeRepresentations: []) let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []) let _ = enqueueMessages(account: engine.account, peerId: engine.account.peerId, messages: [message]).start() diff --git a/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift b/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift index 1c252591aa..cd1fc49ef7 100644 --- a/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift +++ b/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift @@ -182,7 +182,7 @@ final class InstantImageGalleryItemNode: ZoomableContentGalleryItemNode { }) } else { self.imageNode.setSignal(chatMessagePhoto(postbox: self.context.account.postbox, userLocation: userLocation, photoReference: imageReference), dispatchOnDisplayLink: false) - self.fetchDisposable.set(fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: imageReference.resourceReference(largestSize.resource)).start()) + self.fetchDisposable.set(self.context.engine.resources.fetch(reference: imageReference.resourceReference(largestSize.resource), userLocation: userLocation, userContentType: .image).start()) } } else { self._ready.set(.single(Void())) @@ -338,7 +338,7 @@ final class InstantImageGalleryItemNode: ZoomableContentGalleryItemNode { if let (context, media) = self.contextAndMedia, let fileReference = media.concrete(TelegramMediaFile.self) { if isVisible { - self.fetchDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: self.userLocation ?? .other, userContentType: .file, reference: fileReference.resourceReference(fileReference.media.resource)).start()) + self.fetchDisposable.set(context.engine.resources.fetch(reference: fileReference.resourceReference(fileReference.media.resource), userLocation: self.userLocation ?? .other, userContentType: .file).start()) } else { self.fetchDisposable.set(nil) } diff --git a/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift b/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift index 6817519046..fc3d3b47f9 100644 --- a/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift @@ -68,7 +68,7 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode, Galler self.addSubnode(self.videoNode) if case let .file(file) = media.media { - self.fetchedDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: userLocation, userContentType: .video, reference: AnyMediaReference.webPage(webPage: WebpageReference(webPage), media: file).resourceReference(file.resource)).start()) + self.fetchedDisposable.set(context.engine.resources.fetch(reference: AnyMediaReference.webPage(webPage: WebpageReference(webPage), media: file).resourceReference(file.resource), userLocation: userLocation, userContentType: .video).start()) self.statusDisposable.set((context.engine.resources.status(resource: EngineMediaResource(file.resource)) |> deliverOnMainQueue).start(next: { [weak self] status in displayLinkDispatcher.dispatch { diff --git a/submodules/PassportUI/Sources/SecureIdAuthController.swift b/submodules/PassportUI/Sources/SecureIdAuthController.swift index db569570db..c0a66f7147 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthController.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthController.swift @@ -227,7 +227,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable } let primaryLanguageByCountry = configuration.nativeLanguageByCountry - return .single(SecureIdEncryptedFormData(form: form, primaryLanguageByCountry: primaryLanguageByCountry, accountPeer: accountPeer._asPeer(), servicePeer: servicePeer._asPeer())) + return .single(SecureIdEncryptedFormData(form: form, primaryLanguageByCountry: primaryLanguageByCountry, accountPeer: accountPeer, servicePeer: servicePeer)) } } |> deliverOnMainQueue).start(next: { [weak self] formData in @@ -268,7 +268,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable case .form: break case var .list(list): - list.accountPeer = accountPeer._asPeer() + list.accountPeer = accountPeer list.primaryLanguageByCountry = primaryLanguageByCountry list.encryptedValues = values return .list(list) diff --git a/submodules/PassportUI/Sources/SecureIdAuthControllerNode.swift b/submodules/PassportUI/Sources/SecureIdAuthControllerNode.swift index a4e0ff2753..cc9c224885 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthControllerNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthControllerNode.swift @@ -303,7 +303,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { current.updateValues(formData.values) contentNode = current } else { - let current = SecureIdAuthFormContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, peer: EnginePeer(encryptedFormData.servicePeer), privacyPolicyUrl: encryptedFormData.form.termsUrl, form: formData, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, openField: { [weak self] field in + let current = SecureIdAuthFormContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, peer: encryptedFormData.servicePeer, privacyPolicyUrl: encryptedFormData.form.termsUrl, form: formData, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, openField: { [weak self] field in if let strongSelf = self { switch field { case .identity, .address: @@ -684,7 +684,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { var currentValue: SecureIdValueWithContext? switch type { case .phone: - if let peer = form.encryptedFormData?.accountPeer as? TelegramUser, let phone = peer.phone, !phone.isEmpty { + if case let .user(peer)? = form.encryptedFormData?.accountPeer, let phone = peer.phone, !phone.isEmpty { immediatelyAvailableValue = .phone(SecureIdPhoneValue(phone: phone)) } currentValue = findValue(formData.values, key: .phone)?.1 @@ -936,7 +936,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { deleteField(.phone) } else { var immediatelyAvailableValue: SecureIdValue? - if let peer = list.accountPeer as? TelegramUser, let phone = peer.phone, !phone.isEmpty { + if case let .user(peer)? = list.accountPeer, let phone = peer.phone, !phone.isEmpty { immediatelyAvailableValue = .phone(SecureIdPhoneValue(phone: phone)) } self.interaction.push(SecureIdPlaintextFormController(context: self.context, secureIdContext: secureIdContext, type: .phone, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in diff --git a/submodules/PassportUI/Sources/SecureIdAuthControllerState.swift b/submodules/PassportUI/Sources/SecureIdAuthControllerState.swift index 632a2de9bb..7a9fb1eaf8 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthControllerState.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthControllerState.swift @@ -6,8 +6,8 @@ import TelegramCore struct SecureIdEncryptedFormData { let form: EncryptedSecureIdForm let primaryLanguageByCountry: [String: String] - let accountPeer: Peer - let servicePeer: Peer + let accountPeer: EnginePeer + let servicePeer: EnginePeer } enum SecureIdAuthPasswordChallengeState { @@ -64,7 +64,7 @@ struct SecureIdAuthControllerFormState: Equatable { } struct SecureIdAuthControllerListState: Equatable { - var accountPeer: Peer? + var accountPeer: EnginePeer? var twoStepEmail: String? var verificationState: SecureIdAuthControllerVerificationState? var encryptedValues: EncryptedAllSecureIdValues? @@ -73,7 +73,7 @@ struct SecureIdAuthControllerListState: Equatable { var removingValues: Bool = false static func ==(lhs: SecureIdAuthControllerListState, rhs: SecureIdAuthControllerListState) -> Bool { - if !arePeersEqual(lhs.accountPeer, rhs.accountPeer) { + if lhs.accountPeer != rhs.accountPeer { return false } if let lhsTwoStepEmail = lhs.twoStepEmail, let rhsTwoStepEmail = rhs.twoStepEmail, lhsTwoStepEmail != rhsTwoStepEmail { diff --git a/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift b/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift index 732cfa73af..5744bbaedf 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift @@ -52,8 +52,8 @@ final class SecureIdAuthHeaderNode: ASDisplayNode { func updateState(formData: SecureIdEncryptedFormData?, verificationState: SecureIdAuthControllerVerificationState) { if let formData = formData { - self.serviceAvatarNode.setPeer(context: self.context, theme: self.theme, peer: EnginePeer(formData.servicePeer)) - let titleData = self.strings.Passport_RequestHeader(EnginePeer(formData.servicePeer).displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder)) + self.serviceAvatarNode.setPeer(context: self.context, theme: self.theme, peer: formData.servicePeer) + let titleData = self.strings.Passport_RequestHeader(formData.servicePeer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder)) let titleString = NSMutableAttributedString() titleString.append(NSAttributedString(string: titleData.string, font: textFont, textColor: self.theme.list.freeTextColor)) diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift index 80b75bf3d0..3b05051ac9 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift @@ -233,11 +233,11 @@ public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryE public func initialAvatarGalleryEntries(account: Account, engine: TelegramEngine, peer: EnginePeer) -> Signal<[AvatarGalleryEntry]?, NoError> { var initialEntries: [AvatarGalleryEntry] = [] - if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer._asPeer()) { + if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) { initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), [], peer, nil, nil, nil)) } - guard let peerReference = PeerReference(peer._asPeer()) else { + guard let peerReference = PeerReference(peer) else { return .single(initialEntries) } switch peer { @@ -283,7 +283,7 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account var result: [AvatarGalleryEntry] = [] if photos.isEmpty { result = initialEntries - } else if let peerReference = PeerReference(peer._asPeer()) { + } else if let peerReference = PeerReference(peer) { var index: Int32 = 0 if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) { var initialMediaIds = Set() @@ -339,13 +339,13 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account let initialEntries = [firstEntry] if photos.isEmpty { result = initialEntries - if let lastEntry, let firstEntry = result.first, firstEntry.videoRepresentations.isEmpty, !lastEntry.videoRepresentations.isEmpty, avatarGalleryEntryMatchesImage(firstEntry, lastEntry), let peerReference = PeerReference(peer._asPeer()) { + if let lastEntry, let firstEntry = result.first, firstEntry.videoRepresentations.isEmpty, !lastEntry.videoRepresentations.isEmpty, avatarGalleryEntryMatchesImage(firstEntry, lastEntry), let peerReference = PeerReference(peer) { let videoRepresentations = lastEntry.videoRepresentations.map { VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) } result[0] = avatarGalleryEntryWithVideoRepresentations(firstEntry, videoRepresentations: videoRepresentations, immediateThumbnailData: firstEntry.immediateThumbnailData ?? lastEntry.immediateThumbnailData) } - } else if let peerReference = PeerReference(peer._asPeer()) { + } else if let peerReference = PeerReference(peer) { var index: Int32 = 0 if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) { @@ -846,7 +846,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr } else { } case let .image(_, reference, _, _, _, _, _, _, _, _, _, _): - if self.peer.id == self.context.account.peerId, let peerReference = PeerReference(self.peer._asPeer()) { + if self.peer.id == self.context.account.peerId, let peerReference = PeerReference(self.peer) { if let reference = reference { let _ = (self.context.engine.accountData.updatePeerPhotoExisting(reference: reference) |> deliverOnMainQueue).start(next: { [weak self] photo in diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift index 7bea9520d7..acae546563 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift @@ -125,7 +125,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode { } if let peer = peer { - canShare = !peer._asPeer().isCopyProtectionEnabled + canShare = !peer.isCopyProtectionEnabled } default: break diff --git a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift index 81b3980fd6..d6fed42e2b 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift @@ -185,7 +185,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { if let strongSelf = self, let entry = strongSelf.entry, !entry.representations.isEmpty { let subject: ShareControllerSubject var actionCompletionText: String? - if let video = entry.videoRepresentations.last, let peerReference = PeerReference(peer._asPeer()) { + if let video = entry.videoRepresentations.last, let peerReference = PeerReference(peer) { let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil, videoCodec: nil)], alternativeRepresentations: [])) subject = .media(videoFileReference.abstract, nil) actionCompletionText = strongSelf.presentationData.strings.Gallery_VideoSaved @@ -260,7 +260,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { self.zoomableContent = (largestSize.dimensions.cgSize, self.contentNode) if let largestIndex = representations.firstIndex(where: { $0.representation == largestSize }) { - self.fetchDisposable.set(fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: .other, userContentType: .image, reference: representations[largestIndex].reference).start()) + self.fetchDisposable.set(self.context.engine.resources.fetch(reference: representations[largestIndex].reference, userLocation: .other, userContentType: .image).start()) } var id: Int64 @@ -274,7 +274,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { id = id &+ resource.photoId } } - if let video = entry.videoRepresentations.last, let peerReference = PeerReference(self.peer._asPeer()) { + if let video = entry.videoRepresentations.last, let peerReference = PeerReference(self.peer) { if video != previousVideoRepresentations?.last { let mediaManager = self.context.sharedContext.mediaManager let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: entry.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil, videoCodec: nil)], alternativeRepresentations: [])) @@ -610,7 +610,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { } if let largestIndex = representations.firstIndex(where: { $0.representation == largestSize }) { - self.fetchDisposable.set(fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: .other, userContentType: .image, reference: representations[largestIndex].reference).start()) + self.fetchDisposable.set(self.context.engine.resources.fetch(reference: representations[largestIndex].reference, userLocation: .other, userContentType: .image).start()) } default: break diff --git a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index 6d2ebebd3c..f9d6c51e3a 100644 --- a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift @@ -534,7 +534,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode { self.didSetReady = true self.isReady.set(.single(true)) } - } else if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer._asPeer()) { + } else if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer) { let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil, videoCodec: nil)], alternativeRepresentations: [])) let videoContent = NativeVideoContent(id: .profileVideo(id, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: fullSizeOnly, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, storeAfterDownload: nil) diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index afc282dcf6..032111510f 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -1475,7 +1475,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta let (limits, premiumLimits) = data let isPremium = accountPeer?.isPremium ?? false - let hasAdditionalUsernames = (peer?._asPeer().usernames.firstIndex(where: { !$0.flags.contains(.isEditable) }) ?? nil) != nil + let hasAdditionalUsernames = (peer?.usernames.firstIndex(where: { !$0.flags.contains(.isEditable) }) ?? nil) != nil if let peers = peers { let count = Int32(peers.count) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift index def9563495..65e1e35966 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import AsyncDisplayKit import Display -import Postbox import TelegramCore import TelegramPresentationData import AppBundle @@ -239,11 +238,11 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode { strongSelf.animateInAnimationNode = nil } - self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: item.appearAnimation._parse().resource)).start() - self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: item.stillAnimation._parse().resource)).start() - self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: item.listAnimation._parse().resource)).start() + self.fetchStickerDisposable = context.engine.resources.fetch(reference: .standalone(resource: item.appearAnimation._parse().resource), userLocation: .other, userContentType: .sticker).start() + self.fetchStickerDisposable = context.engine.resources.fetch(reference: .standalone(resource: item.stillAnimation._parse().resource), userLocation: .other, userContentType: .sticker).start() + self.fetchStickerDisposable = context.engine.resources.fetch(reference: .standalone(resource: item.listAnimation._parse().resource), userLocation: .other, userContentType: .sticker).start() if let applicationAnimation = item.applicationAnimation { - self.fetchFullAnimationDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: applicationAnimation._parse().resource)).start() + self.fetchFullAnimationDisposable = context.engine.resources.fetch(reference: .standalone(resource: applicationAnimation._parse().resource), userLocation: .other, userContentType: .sticker).start() } if self.isLocked { diff --git a/submodules/SettingsUI/Sources/Notifications/NotificationsAndSoundsController.swift b/submodules/SettingsUI/Sources/Notifications/NotificationsAndSoundsController.swift index 9a634047c6..3dc9a053c2 100644 --- a/submodules/SettingsUI/Sources/Notifications/NotificationsAndSoundsController.swift +++ b/submodules/SettingsUI/Sources/Notifications/NotificationsAndSoundsController.swift @@ -794,7 +794,7 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions for (key, value) in list.settings { if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != context.account.peerId { if value.storySettings != defaultStorySettings { - stories[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + stories[key] = NotificationExceptionWrapper(settings: value, peer: peer) } switch value.muteState { @@ -805,24 +805,24 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions default: switch key.namespace { case Namespaces.Peer.CloudUser: - users[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + users[key] = NotificationExceptionWrapper(settings: value, peer: peer) default: - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + if case let .channel(peer) = peer, case .broadcast = peer.info { channels[key] = NotificationExceptionWrapper(settings: value, peer: .channel(peer)) } else { - groups[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + groups[key] = NotificationExceptionWrapper(settings: value, peer: peer) } } } default: switch key.namespace { case Namespaces.Peer.CloudUser: - users[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + users[key] = NotificationExceptionWrapper(settings: value, peer: peer) default: - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + if case let .channel(peer) = peer, case .broadcast = peer.info { channels[key] = NotificationExceptionWrapper(settings: value, peer: .channel(peer)) } else { - groups[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + groups[key] = NotificationExceptionWrapper(settings: value, peer: peer) } } } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/BlockedPeersController.swift b/submodules/SettingsUI/Sources/Privacy and Security/BlockedPeersController.swift index 182ca30ba9..bcde257640 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/BlockedPeersController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/BlockedPeersController.swift @@ -18,9 +18,9 @@ private final class BlockedPeersControllerArguments { let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void let addPeer: () -> Void let removePeer: (PeerId) -> Void - let openPeer: (Peer) -> Void - - init(context: AccountContext, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (Peer) -> Void) { + let openPeer: (EnginePeer) -> Void + + init(context: AccountContext, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (EnginePeer) -> Void) { self.context = context self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.addPeer = addPeer @@ -41,7 +41,7 @@ private enum BlockedPeersEntryStableId: Hashable { private enum BlockedPeersEntry: ItemListNodeEntry { case add(PresentationTheme, String) - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, EnginePeer, ItemListPeerItemEditing, Bool) var section: ItemListSectionId { switch self { @@ -86,7 +86,7 @@ private enum BlockedPeersEntry: ItemListNodeEntry { if lhsNameOrder != rhsNameOrder { return false } - if !lhsPeer.isEqual(rhsPeer) { + if lhsPeer != rhsPeer { return false } if lhsEditing != rhsEditing { @@ -132,7 +132,7 @@ private enum BlockedPeersEntry: ItemListNodeEntry { arguments.removePeer(peer.id) })]) - return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: nil, text: .none, label: .none, editing: editing, revealOptions: revealOptions, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: { + return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: .none, label: .none, editing: editing, revealOptions: revealOptions, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: { arguments.openPeer(peer) }, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) @@ -195,7 +195,7 @@ private func blockedPeersControllerEntries(presentationData: PresentationData, s var index: Int32 = 0 for peer in blockedPeersState.peers { - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer.peer!, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.peerId == state.peerIdWithRevealedOptions), state.removingPeerId != peer.peerId)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, EnginePeer(peer.peer!), ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.peerId == state.peerIdWithRevealedOptions), state.removingPeerId != peer.peerId)) index += 1 } } @@ -267,7 +267,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext: } })) }, openPeer: { peer in - if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: EnginePeer(peer), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { pushControllerImpl?(controller) } }) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift index 0d423438f9..f390f231b6 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift @@ -340,7 +340,7 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP } } - updatedPeers[peer.id] = SelectivePrivacyPeer(peer: peer._asPeer(), participantCount: participantCount) + updatedPeers[peer.id] = SelectivePrivacyPeer(peer: peer, participantCount: participantCount) } } return updatedPeers diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift index 1dc5be3814..39380763fc 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift @@ -3,7 +3,6 @@ import UIKit import Display import AsyncDisplayKit import SwiftSignalKit -import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences @@ -36,7 +35,7 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem { let dateTimeFormat: PresentationDateTimeFormat let nameDisplayOrder: PresentationPersonNameOrder let website: WebAuthorization - let peer: Peer? + let peer: EnginePeer? let enabled: Bool let editing: Bool let revealed: Bool @@ -45,7 +44,7 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem { let removeSession: (Int64) -> Void let action: (() -> Void)? - init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, action: (() -> Void)?) { + init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: EnginePeer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, action: (() -> Void)?) { self.context = context self.presentationData = presentationData self.systemStyle = systemStyle @@ -229,8 +228,8 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { let rightInset: CGFloat = params.rightInset - if let user = item.peer as? TelegramUser { - titleAttributedString = NSAttributedString(string: EnginePeer(user).displayTitle(strings: item.presentationData.strings, displayOrder: item.nameDisplayOrder), font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) + if let peer = item.peer, case .user = peer { + titleAttributedString = NSAttributedString(string: peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.nameDisplayOrder), font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) } var appString = "" @@ -332,7 +331,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { } if let peer = item.peer { - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(peer), authorOfMessage: nil, overrideImage: nil, emptyColor: nil, clipStyle: .none, synchronousLoad: false) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, authorOfMessage: nil, overrideImage: nil, emptyColor: nil, clipStyle: .none, synchronousLoad: false) } let revealOffset = strongSelf.revealOffset diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift index a8cee09181..a2cd1fcee2 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift @@ -21,7 +21,7 @@ private final class RecentSessionsControllerArguments { let terminateOtherSessions: () -> Void let openSession: (RecentAccountSession) -> Void - let openWebSession: (WebAuthorization, Peer?) -> Void + let openWebSession: (WebAuthorization, EnginePeer?) -> Void let removeWebSession: (Int64) -> Void let terminateAllWebSessions: () -> Void @@ -34,7 +34,7 @@ private final class RecentSessionsControllerArguments { let openDesktopLink: () -> Void let openWebLink: () -> Void - init(context: AccountContext, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, terminateOtherSessions: @escaping () -> Void, openSession: @escaping (RecentAccountSession) -> Void, openWebSession: @escaping (WebAuthorization, Peer?) -> Void, removeWebSession: @escaping (Int64) -> Void, terminateAllWebSessions: @escaping () -> Void, addDevice: @escaping () -> Void, openOtherAppsUrl: @escaping () -> Void, setupAuthorizationTTL: @escaping () -> Void, openDesktopLink: @escaping () -> Void, openWebLink: @escaping () -> Void) { + init(context: AccountContext, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void, terminateOtherSessions: @escaping () -> Void, openSession: @escaping (RecentAccountSession) -> Void, openWebSession: @escaping (WebAuthorization, EnginePeer?) -> Void, removeWebSession: @escaping (Int64) -> Void, terminateAllWebSessions: @escaping () -> Void, addDevice: @escaping () -> Void, openOtherAppsUrl: @escaping () -> Void, setupAuthorizationTTL: @escaping () -> Void, openDesktopLink: @escaping () -> Void, openWebLink: @escaping () -> Void) { self.context = context self.setSessionIdWithRevealedOptions = setSessionIdWithRevealedOptions self.removeSession = removeSession @@ -117,7 +117,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry { case otherSessionsHeader(SortIndex, String) case addDevice(SortIndex, String) case session(index: Int32, sortIndex: SortIndex, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editing: Bool, revealed: Bool) - case website(index: Int32, sortIndex: SortIndex, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool) + case website(index: Int32, sortIndex: SortIndex, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: EnginePeer?, enabled: Bool, editing: Bool, revealed: Bool) case devicesInfo(SortIndex, String) case ttlHeader(SortIndex, String) case ttlTimeout(SortIndex, String, String) @@ -296,7 +296,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry { return false } case let .website(lhsIndex, lhsSortIndex, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsWebsite, lhsPeer, lhsEnabled, lhsEditing, lhsRevealed): - if case let .website(rhsIndex, rhsSortIndex, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsSortIndex == rhsSortIndex, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameOrder == rhsNameOrder, lhsWebsite == rhsWebsite, arePeersEqual(lhsPeer, rhsPeer), lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed { + if case let .website(rhsIndex, rhsSortIndex, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsSortIndex == rhsSortIndex, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameOrder == rhsNameOrder, lhsWebsite == rhsWebsite, lhsPeer == rhsPeer, lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed { return true } else { return false @@ -562,7 +562,7 @@ private func recentSessionsControllerEntries(presentationData: PresentationData, let website = websites[i] if !existingSessionIds.contains(website.hash) { existingSessionIds.insert(website.hash) - entries.append(.website(index: Int32(i), sortIndex: SortIndex(section: 1, item: i), strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, website: website, peer: peers[website.botId], enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash)) + entries.append(.website(index: Int32(i), sortIndex: SortIndex(section: 1, item: i), strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, website: website, peer: peers[website.botId].flatMap(EnginePeer.init), enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash)) } } } @@ -727,7 +727,7 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont }) presentControllerImpl?(controller, nil) }, openWebSession: { session, peer in - let controller = RecentSessionScreen(context: context, subject: .website(session, peer.flatMap(EnginePeer.init)), updateAcceptSecretChats: { _ in }, updateAcceptIncomingCalls: { _ in }, remove: { completion in + let controller = RecentSessionScreen(context: context, subject: .website(session, peer), updateAcceptSecretChats: { _ in }, updateAcceptIncomingCalls: { _ in }, remove: { completion in removeWebSessionImpl(session.hash) completion() }) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index 8c9b007e92..cce4170d0c 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -1478,7 +1478,7 @@ public func selectivePrivacySettingsController( } } - updatedPeers[peer.id] = SelectivePrivacyPeer(peer: peer._asPeer(), participantCount: participantCount) + updatedPeers[peer.id] = SelectivePrivacyPeer(peer: peer, participantCount: participantCount) } } return updatedPeers diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift index f229031d1d..6395e90a2f 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift @@ -475,7 +475,7 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri } } - updatedPeers.append(SelectivePrivacyPeer(peer: peer._asPeer(), participantCount: participantCount)) + updatedPeers.append(SelectivePrivacyPeer(peer: peer, participantCount: participantCount)) } } return updatedPeers diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index 86feeec884..560caa33be 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -1551,7 +1551,7 @@ private func notificationSearchableItems(context: AccountContext, settings: Glob for (key, value) in list.settings { if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != context.account.peerId { if value.storySettings != defaultStorySettings { - stories[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + stories[key] = NotificationExceptionWrapper(settings: value, peer: peer) } switch value.muteState { @@ -1562,24 +1562,24 @@ private func notificationSearchableItems(context: AccountContext, settings: Glob default: switch key.namespace { case Namespaces.Peer.CloudUser: - users[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + users[key] = NotificationExceptionWrapper(settings: value, peer: peer) default: - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + if case let .channel(peer) = peer, case .broadcast = peer.info { channels[key] = NotificationExceptionWrapper(settings: value, peer: .channel(peer)) } else { - groups[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + groups[key] = NotificationExceptionWrapper(settings: value, peer: peer) } } } default: switch key.namespace { case Namespaces.Peer.CloudUser: - users[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + users[key] = NotificationExceptionWrapper(settings: value, peer: peer) default: - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + if case let .channel(peer) = peer, case .broadcast = peer.info { channels[key] = NotificationExceptionWrapper(settings: value, peer: .channel(peer)) } else { - groups[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer)) + groups[key] = NotificationExceptionWrapper(settings: value, peer: peer) } } } diff --git a/submodules/SettingsUI/Sources/ThemePickerController.swift b/submodules/SettingsUI/Sources/ThemePickerController.swift index b40046b0a5..8be71db7d0 100644 --- a/submodules/SettingsUI/Sources/ThemePickerController.swift +++ b/submodules/SettingsUI/Sources/ThemePickerController.swift @@ -3,7 +3,6 @@ import UIKit import AsyncDisplayKit import Display import SwiftSignalKit -import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences @@ -1230,7 +1229,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme wallpaperSignal = cachedWallpaper(account: context.account, slug: file.slug, settings: colorWallpaper.settings) |> mapToSignal { cachedWallpaper in if let wallpaper = cachedWallpaper?.wallpaper, case let .file(file) = wallpaper { - let _ = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)).start() + let _ = context.engine.resources.fetch(reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource), userLocation: .other, userContentType: .other).start() return .single(wallpaper) diff --git a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift index a0733a4bcf..ad9cc43121 100644 --- a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift +++ b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift @@ -276,7 +276,7 @@ private final class ThemeGridThemeItemIconNode : ASDisplayNode { animatedStickerNode.autoplay = true animatedStickerNode.visibility = true - self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: item.context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource)).start()) + self.stickerFetchedDisposable.set(item.context.engine.resources.fetch(reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource), userLocation: .other, userContentType: .other).start()) let thumbnailDimensions = PixelDimensions(width: 512, height: 512) self.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: file.immediateThumbnailData, size: emojiFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: thumbnailDimensions.cgSize) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index ffe197cc66..60575fb189 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -3,7 +3,6 @@ import UIKit import AsyncDisplayKit import Display import SwiftSignalKit -import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences @@ -425,7 +424,7 @@ private func themeSettingsControllerEntries( var authorName = presentationData.strings.Appearance_PreviewReplyAuthor if let accountPeer { nameColor = accountPeer.nameColor ?? .preset(.blue) - if accountPeer._asPeer().hasCustomNameColor { + if accountPeer.hasCustomNameColor { authorName = accountPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) } profileColor = accountPeer.effectiveProfileColor @@ -1323,7 +1322,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The wallpaperSignal = cachedWallpaper(account: context.account, slug: file.slug, settings: colorWallpaper.settings) |> mapToSignal { cachedWallpaper in if let wallpaper = cachedWallpaper?.wallpaper, case let .file(file) = wallpaper { - let _ = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)).start() + let _ = context.engine.resources.fetch(reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource), userLocation: .other, userContentType: .other).start() return .single(wallpaper) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index d1b5c35f48..d45899e01c 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -299,18 +299,18 @@ public final class ShareControllerAppEnvironment: ShareControllerEnvironment { public final class ShareControllerSwitchableAccount: Equatable { public let account: ShareControllerAccountContext - public let peer: Peer - - public init(account: ShareControllerAccountContext, peer: Peer) { + public let peer: EnginePeer + + public init(account: ShareControllerAccountContext, peer: EnginePeer) { self.account = account self.peer = peer } - + public static func ==(lhs: ShareControllerSwitchableAccount, rhs: ShareControllerSwitchableAccount) -> Bool { if lhs.account !== rhs.account { return false } - if !arePeersEqual(lhs.peer, rhs.peer) { + if lhs.peer != rhs.peer { return false } return true @@ -1147,8 +1147,8 @@ public final class ShareController: ViewController { accountPeerId: info.account.accountPeerId, stateManager: info.account.stateManager, contentSettings: info.account.contentSettings, - peer: EnginePeer(info.peer), - title: EnginePeer(info.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), + peer: info.peer, + title: info.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), isSelected: info.account.accountId == strongSelf.currentContext.accountId, strings: presentationData.strings, theme: presentationData.theme, diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift index 7f82934154..1bc44a39f9 100644 --- a/submodules/ShareController/Sources/SharePeersContainerNode.swift +++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift @@ -232,7 +232,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { network: context.stateManager.network, contentSettings: context.contentSettings, theme: theme, - peer: EnginePeer(info.peer), + peer: info.peer, emptyColor: nil, synchronousLoad: false ) diff --git a/submodules/ShareController/Sources/ShareSearchContainerNode.swift b/submodules/ShareController/Sources/ShareSearchContainerNode.swift index 708bc1a46c..faef506e64 100644 --- a/submodules/ShareController/Sources/ShareSearchContainerNode.swift +++ b/submodules/ShareController/Sources/ShareSearchContainerNode.swift @@ -475,7 +475,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { var index = 0 for (peer, requiresPremiumForMessaging) in recentPeerList { if let mainPeer = peer.peers[peer.peerId], canSendMessagesToPeer(mainPeer) { - recentItemList.append(.peer(index: index, theme: theme, peer: mainPeer, associatedPeer: mainPeer._asPeer().associatedPeerId.flatMap { peer.peers[$0] }, presence: nil, requiresPremiumForMessaging: requiresPremiumForMessaging, requiresStars: nil, strings: strings)) + recentItemList.append(.peer(index: index, theme: theme, peer: mainPeer, associatedPeer: mainPeer.associatedPeerId.flatMap { peer.peers[$0] }, presence: nil, requiresPremiumForMessaging: requiresPremiumForMessaging, requiresStars: nil, strings: strings)) index += 1 } } diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index f470163bb9..0fa30c747c 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -205,7 +205,7 @@ private enum StatsEntry: ItemListNodeEntry { case instantPageInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) case postsTitle(PresentationTheme, String) - case post(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, StatsPostItem, ChannelStatsPostInteractions) + case post(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, EnginePeer, StatsPostItem, ChannelStatsPostInteractions) case boostLevel(PresentationTheme, Int32, Int32, CGFloat) @@ -636,7 +636,7 @@ private enum StatsEntry: ItemListNodeEntry { return false } case let .post(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsPost, lhsInteractions): - if case let .post(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsPost, rhsInteractions) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsPost == rhsPost, lhsInteractions == rhsInteractions { + if case let .post(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsPost, rhsInteractions) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsPeer == rhsPeer, lhsPost == rhsPost, lhsInteractions == rhsInteractions { return true } else { return false @@ -960,7 +960,7 @@ private enum StatsEntry: ItemListNodeEntry { }, sectionId: self.section, style: .blocks) case let .post(_, _, _, _, peer, post, interactions): return StatsMessageItem(context: arguments.context, presentationData: presentationData, peer: peer, item: post, views: interactions.views, reactions: interactions.reactions, forwards: interactions.forwards, sectionId: self.section, style: .blocks, action: { - arguments.openPostStats(EnginePeer(peer), post) + arguments.openPostStats(peer, post) }, openStory: { sourceView in if case let .story(_, story) = post { arguments.openStory(story, sourceView) @@ -1426,11 +1426,11 @@ private func statsEntries( switch post { case let .message(message): if let interactions = interactions[.message(id: message.id)] { - entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer._asPeer(), post, interactions)) + entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, post, interactions)) } case let .story(_, story): if let interactions = interactions[.story(peerId: peer.id, id: story.id)] { - entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer._asPeer(), post, interactions)) + entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, post, interactions)) } } index += 1 diff --git a/submodules/StatisticsUI/Sources/MessageStatsController.swift b/submodules/StatisticsUI/Sources/MessageStatsController.swift index 68fcee6105..bbe3db555e 100644 --- a/submodules/StatisticsUI/Sources/MessageStatsController.swift +++ b/submodules/StatisticsUI/Sources/MessageStatsController.swift @@ -198,10 +198,10 @@ private enum StatsEntry: ItemListNodeEntry { var reactions: Int32 = 0 var isStory = false - let peer: Peer + let peer: EnginePeer switch item { case let .message(message): - peer = message.peers[message.id.peerId]! + peer = EnginePeer(message.peers[message.id.peerId]!) for attribute in message.attributes { if let viewsAttribute = attribute as? ViewCountMessageAttribute { views = Int32(viewsAttribute.count) @@ -214,7 +214,7 @@ private enum StatsEntry: ItemListNodeEntry { } } case let .story(peerValue, story): - peer = peerValue._asPeer() + peer = peerValue views = Int32(story.views?.seenCount ?? 0) forwards = Int32(story.views?.forwardCount ?? 0) reactions = Int32(story.views?.reactedCount ?? 0) diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index b7aa9dc8fb..2751c95efd 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -19,7 +19,7 @@ public class StatsMessageItem: ListViewItem, ItemListItem { let context: AccountContext let presentationData: ItemListPresentationData let systemStyle: ItemListSystemStyle - let peer: Peer + let peer: EnginePeer let item: StatsPostItem let views: Int32 let reactions: Int32 @@ -31,7 +31,7 @@ public class StatsMessageItem: ListViewItem, ItemListItem { let openStory: (UIView) -> Void let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? - init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .glass, peer: Peer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { + init(context: AccountContext, presentationData: ItemListPresentationData, systemStyle: ItemListSystemStyle = .glass, peer: EnginePeer, item: StatsPostItem, views: Int32, reactions: Int32, forwards: Int32, isPeer: Bool = false, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, openStory: @escaping (UIView) -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) { self.context = context self.presentationData = presentationData self.systemStyle = systemStyle @@ -351,7 +351,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { } if item.isPeer { - text = EnginePeer(item.peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + text = item.peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) } else { text = foldLineBreaks(text) } @@ -476,7 +476,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { strongSelf.offsetContainerNode.addSubnode(avatarNode) strongSelf.avatarNode = avatarNode } - avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer)) + avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer) if case .story = item.item { contentImageInset += 3.0 diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift index f0ea64fe0d..86db813a19 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift @@ -626,7 +626,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP self.peerAvatarDisposable?.dispose() let size = CGSize(width: 128.0, height: 128.0) - if let representation = peer.largeProfileImage, let signal = peerAvatarImage(account: self.call.context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: representation, displayDimensions: size, synchronousLoad: self.callScreenState?.avatarImage == nil) { + if let representation = peer.largeProfileImage, let signal = peerAvatarImage(account: self.call.context.account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: representation, displayDimensions: size, synchronousLoad: self.callScreenState?.avatarImage == nil) { self.peerAvatarDisposable = (signal |> deliverOnMainQueue).startStrict(next: { [weak self] imageVersions in guard let self else { diff --git a/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift index 4c6ef24fcc..128651bb7e 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift @@ -350,7 +350,7 @@ final class VideoChatParticipantVideoComponent: Component { if self.blurredAvatarDisposable == nil { //TODO:release synchronous - if let participantPeer = component.participant.peer, let imageCache = component.call.accountContext.imageCache as? DirectMediaImageCache, let peerReference = PeerReference(participantPeer._asPeer()) { + if let participantPeer = component.participant.peer, let imageCache = component.call.accountContext.imageCache as? DirectMediaImageCache, let peerReference = PeerReference(participantPeer) { if let result = imageCache.getAvatarImage(peer: peerReference, resource: MediaResourceReference.avatar(peer: peerReference, resource: smallProfileImage.resource), immediateThumbnail: participantPeer.profileImageRepresentations.first?.immediateThumbnailData, size: 64, synchronous: false) { if let image = result.image { blurredAvatarView.image = blurredAvatarImage(image) diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift index f13fd807fd..0bfe632b7e 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift @@ -551,7 +551,7 @@ extension VideoChatScreenComponent.View { let peerId = callState.myPeerId let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) - currentCall.accountContext.account.postbox.mediaBox.storeResourceData(resource.id, data: data) + currentCall.accountContext.engine.resources.storeResourceData(id: EngineMediaResource.Id(resource.id), data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false) self.currentUpdatingAvatar = (representation, 0.0) @@ -592,7 +592,7 @@ extension VideoChatScreenComponent.View { let peerId = callState.myPeerId let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) - currentCall.accountContext.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) + currentCall.accountContext.engine.resources.storeResourceData(id: EngineMediaResource.Id(photoResource.id), data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false) self.currentUpdatingAvatar = (representation, 0.0) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift b/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift index fe3f0c60ce..9688ba3d0d 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift @@ -139,7 +139,7 @@ public final class VoiceChatJoinScreen: ViewController { strongSelf.dismiss() strongSelf.join(activeCall) } else { - strongSelf.controllerNode.setPeer(call: activeCall, peer: peer._asPeer(), title: call.info.title, memberCount: call.info.participantCount, isStream: call.info.isStream) + strongSelf.controllerNode.setPeer(call: activeCall, peer: peer, title: call.info.title, memberCount: call.info.participantCount, isStream: call.info.isStream) } } else { let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -622,7 +622,7 @@ public final class VoiceChatJoinScreen: ViewController { })) } - func setPeer(call: CachedChannelData.ActiveCall, peer: Peer, title: String?, memberCount: Int, isStream: Bool) { + func setPeer(call: CachedChannelData.ActiveCall, peer: EnginePeer, title: String?, memberCount: Int, isStream: Bool) { self.call = call let transition = ContainedViewLayoutTransition.animated(duration: 0.22, curve: .easeInOut) @@ -647,7 +647,7 @@ final class VoiceChatPreviewContentNode: ASDisplayNode, ShareContentContainerNod private let titleNode: ImmediateTextNode private let countNode: ImmediateTextNode - init(context: AccountContext, peer: Peer, title: String?, memberCount: Int, isStream: Bool, theme: PresentationTheme, strings: PresentationStrings, displayOrder: PresentationPersonNameOrder) { + init(context: AccountContext, peer: EnginePeer, title: String?, memberCount: Int, isStream: Bool, theme: PresentationTheme, strings: PresentationStrings, displayOrder: PresentationPersonNameOrder) { self.avatarNode = AvatarNode(font: avatarFont) self.titleNode = ImmediateTextNode() self.titleNode.maximumNumberOfLines = 4 @@ -659,10 +659,10 @@ final class VoiceChatPreviewContentNode: ASDisplayNode, ShareContentContainerNod super.init() self.addSubnode(self.avatarNode) - self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), emptyColor: theme.list.mediaPlaceholderColor) + self.avatarNode.setPeer(context: context, theme: theme, peer: peer, emptyColor: theme.list.mediaPlaceholderColor) self.addSubnode(self.titleNode) - self.titleNode.attributedText = NSAttributedString(string: title ?? EnginePeer(peer).displayTitle(strings: strings, displayOrder: displayOrder), font: Font.semibold(16.0), textColor: theme.actionSheet.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: title ?? peer.displayTitle(strings: strings, displayOrder: displayOrder), font: Font.semibold(16.0), textColor: theme.actionSheet.primaryTextColor) self.addSubnode(self.countNode) diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiUtils.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiUtils.swift index 73f5808f8b..7c585c3d32 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiUtils.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiUtils.swift @@ -14,6 +14,10 @@ public extension PeerReference { return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)) } } + + init?(_ peer: EnginePeer) { + self.init(peer._asPeer()) + } } extension PeerReference { diff --git a/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift b/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift index e8495e3b02..b12e59e661 100644 --- a/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift +++ b/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift @@ -11,6 +11,10 @@ public final class SelectivePrivacyPeer: Equatable { self.peer = peer self.participantCount = participantCount } + + public convenience init(peer: EnginePeer, participantCount: Int32?) { + self.init(peer: peer._asPeer(), participantCount: participantCount) + } public static func ==(lhs: SelectivePrivacyPeer, rhs: SelectivePrivacyPeer) -> Bool { if !lhs.peer.isEqual(rhs.peer) { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChangePeerNotificationSettings.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChangePeerNotificationSettings.swift index fe3110e357..058519ec1a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChangePeerNotificationSettings.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChangePeerNotificationSettings.swift @@ -62,7 +62,7 @@ func _internal_togglePeerMuted(account: Account, peerId: PeerId, threadId: Int64 } } -public func resolvedAreStoriesMuted(globalSettings: GlobalNotificationSettingsSet, peer: Peer, peerSettings: TelegramPeerNotificationSettings?, topSearchPeers: [PeerId]) -> Bool { +public func resolvedAreStoriesMuted(globalSettings: GlobalNotificationSettingsSet, peer: EnginePeer, peerSettings: TelegramPeerNotificationSettings?, topSearchPeers: [PeerId]) -> Bool { let defaultIsMuted: Bool switch globalSettings.privateChats.storySettings.mute { case .muted: @@ -114,7 +114,7 @@ func _internal_togglePeerStoriesMuted(account: Account, peerId: PeerId) -> Signa case .default: let globalNotificationSettings = transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications)?.get(GlobalNotificationSettings.self) ?? GlobalNotificationSettings.defaultSettings - if resolvedAreStoriesMuted(globalSettings: globalNotificationSettings.effective, peer: peer, peerSettings: previousSettings, topSearchPeers: topSearchPeers) { + if resolvedAreStoriesMuted(globalSettings: globalNotificationSettings.effective, peer: EnginePeer(peer), peerSettings: previousSettings, topSearchPeers: topSearchPeers) { storySettings.mute = .unmuted } else { storySettings.mute = .muted diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift index dceedea39a..b6de8a22e1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift @@ -5,17 +5,17 @@ import Postbox import TelegramApi public struct InactiveChannel : Equatable { - public let peer: Peer + public let peer: EnginePeer public let lastActivityDate: Int32 public let participantsCount: Int32? - - init(peer: Peer, lastActivityDate: Int32, participantsCount: Int32?) { + + init(peer: EnginePeer, lastActivityDate: Int32, participantsCount: Int32?) { self.peer = peer self.lastActivityDate = lastActivityDate self.participantsCount = participantsCount } public static func ==(lhs: InactiveChannel, rhs: InactiveChannel) -> Bool { - return lhs.peer.isEqual(rhs.peer) && lhs.lastActivityDate == rhs.lastActivityDate && lhs.participantsCount == rhs.participantsCount + return lhs.peer == rhs.peer && lhs.lastActivityDate == rhs.lastActivityDate && lhs.participantsCount == rhs.participantsCount } } @@ -43,7 +43,7 @@ func _internal_inactiveChannelList(network: Network) -> Signal<[InactiveChannel] } var inactive: [InactiveChannel] = [] for (i, channel) in channels.enumerated() { - inactive.append(InactiveChannel(peer: channel, lastActivityDate: i < dates.count ? dates[i] : 0, participantsCount: participantsCounts[channel.id])) + inactive.append(InactiveChannel(peer: EnginePeer(channel), lastActivityDate: i < dates.count ? dates[i] : 0, participantsCount: participantsCounts[channel.id])) } return inactive } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift index 022d4a046e..23b6e63be8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift @@ -5,14 +5,14 @@ import TelegramApi public final class NotificationExceptionsList: Equatable { - public let peers: [PeerId: Peer] + public let peers: [PeerId: EnginePeer] public let settings: [PeerId: TelegramPeerNotificationSettings] - - public init(peers: [PeerId: Peer], settings: [PeerId: TelegramPeerNotificationSettings]) { + + public init(peers: [PeerId: EnginePeer], settings: [PeerId: TelegramPeerNotificationSettings]) { self.peers = peers self.settings = settings } - + public static func ==(lhs: NotificationExceptionsList, rhs: NotificationExceptionsList) -> Bool { return lhs === rhs } @@ -41,10 +41,10 @@ func _internal_notificationExceptionsList(accountPeerId: PeerId, postbox: Postbo let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: accountPeerId,peers: parsedPeers) - var peers: [PeerId: Peer] = [:] + var peers: [PeerId: EnginePeer] = [:] for id in parsedPeers.allIds { if let peer = transaction.getPeer(id) { - peers[peer.id] = peer + peers[peer.id] = EnginePeer(peer) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift index bc0b5ab1ec..dd54a5fdfa 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift @@ -558,6 +558,26 @@ public extension EnginePeer { var profileBackgroundEmojiId: Int64? { return self._asPeer().profileBackgroundEmojiId } + + var isCopyProtectionEnabled: Bool { + return self._asPeer().isCopyProtectionEnabled + } + + var isMonoForum: Bool { + return self._asPeer().isMonoForum + } + + var associatedPeerId: Id? { + return self._asPeer().associatedPeerId + } + + var hasCustomNameColor: Bool { + return self._asPeer().hasCustomNameColor + } + + func hasSensitiveContent(platform: String) -> Bool { + return self._asPeer().hasSensitiveContent(platform: platform) + } } public extension EnginePeer { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 5875920205..600175ea17 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -1233,14 +1233,11 @@ public extension TelegramEngine { } public func ensurePeerIsLocallyAvailable(peer: EnginePeer) -> Signal { - return _internal_storedMessageFromSearchPeer(postbox: self.account.postbox, peer: peer._asPeer()) - |> map { result -> EnginePeer in - return EnginePeer(result) - } + return _internal_storedMessageFromSearchPeer(postbox: self.account.postbox, peer: peer) } public func ensurePeersAreLocallyAvailable(peers: [EnginePeer]) -> Signal { - return _internal_storedMessageFromSearchPeers(account: self.account, peers: peers.map { $0._asPeer() }) + return _internal_storedMessageFromSearchPeers(account: self.account, peers: peers) } public func mostRecentSecretChat(id: EnginePeer.Id) -> Signal { diff --git a/submodules/TelegramCore/Sources/Utils/CanSendMessagesToPeer.swift b/submodules/TelegramCore/Sources/Utils/CanSendMessagesToPeer.swift index fd3d26b523..00e74c43ae 100644 --- a/submodules/TelegramCore/Sources/Utils/CanSendMessagesToPeer.swift +++ b/submodules/TelegramCore/Sources/Utils/CanSendMessagesToPeer.swift @@ -7,16 +7,15 @@ private final class LinkHelperClass: NSObject { } public func canSendMessagesToPeer(_ peer: EnginePeer, ignoreDefault: Bool = false) -> Bool { - let peer = peer._asPeer() - if let peer = peer as? TelegramUser, peer.addressName == "replies" { - return false - } else if peer is TelegramUser || peer is TelegramGroup { - return !peer.isDeleted - } else if let peer = peer as? TelegramSecretChat { - return peer.embeddedState == .active - } else if let peer = peer as? TelegramChannel { - return peer.hasPermission(.sendSomething, ignoreDefault: ignoreDefault) - } else { + if case let .user(user) = peer, user.addressName == "replies" { return false } + switch peer { + case .user, .legacyGroup: + return !peer.isDeleted + case let .secretChat(secretChat): + return secretChat.embeddedState == .active + case let .channel(channel): + return channel.hasPermission(.sendSomething, ignoreDefault: ignoreDefault) + } } diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index 49eb1535ee..df30e8c937 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -491,6 +491,10 @@ public func peerViewMonoforumMainPeer(_ view: PeerView) -> Peer? { } public extension RenderedPeer { + convenience init(peer: EnginePeer) { + self.init(peer: peer._asPeer()) + } + convenience init(message: Message) { var peers = SimpleDictionary() let peerId = message.id.peerId diff --git a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift index 27e0300312..08a093eecd 100644 --- a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift +++ b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift @@ -2,16 +2,16 @@ import Foundation import Postbox import SwiftSignalKit -public func _internal_storedMessageFromSearchPeer(postbox: Postbox, peer: Peer) -> Signal { - return postbox.transaction { transaction -> Peer in +public func _internal_storedMessageFromSearchPeer(postbox: Postbox, peer: EnginePeer) -> Signal { + return postbox.transaction { transaction -> EnginePeer in if transaction.getPeer(peer.id) == nil { - updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updatedPeer in + updatePeersCustom(transaction: transaction, peers: [peer._asPeer()], update: { _, updatedPeer in return updatedPeer }) } if let group = transaction.getPeer(peer.id) as? TelegramGroup, let migrationReference = group.migrationReference { if let migrationPeer = transaction.getPeer(migrationReference.peerId) { - return migrationPeer + return EnginePeer(migrationPeer) } else { return peer } @@ -20,11 +20,11 @@ public func _internal_storedMessageFromSearchPeer(postbox: Postbox, peer: Peer) } } -func _internal_storedMessageFromSearchPeers(account: Account, peers: [Peer]) -> Signal { +func _internal_storedMessageFromSearchPeers(account: Account, peers: [EnginePeer]) -> Signal { return account.postbox.transaction { transaction -> Void in for peer in peers { if transaction.getPeer(peer.id) == nil { - updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updatedPeer in + updatePeersCustom(transaction: transaction, peers: [peer._asPeer()], update: { _, updatedPeer in return updatedPeer }) } diff --git a/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift index b6e7d92aae..a35099f90c 100644 --- a/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift +++ b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift @@ -366,7 +366,7 @@ final class ChatAdPanelNode: ASDisplayNode { if fileReference.media.isAnimatedSticker { let dimensions = fileReference.media.dimensions ?? PixelDimensions(width: 512, height: 512) updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), file: fileReference.media, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))) - updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(fileReference.media.resource)) + updatedFetchMediaSignal = context.engine.resources.fetch(reference: fileReference.resourceReference(fileReference.media.resource), userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: fileReference.media)) } else if fileReference.media.isVideo || fileReference.media.isAnimated { updateImageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), fileReference: fileReference, blurred: false) } else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) { diff --git a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift index 4c569cd3eb..2d8cc47b63 100644 --- a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift @@ -629,7 +629,7 @@ public func makeAttachmentFileControllerImpl( if let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).start(next: { peer in - guard let peer, let peerReference = PeerReference(peer._asPeer()) else { + guard let peer, let peerReference = PeerReference(peer) else { return } send([.savedMusic(peer: peerReference, media: file)], false, nil, nil) @@ -981,7 +981,7 @@ public func makeAttachmentFileControllerImpl( return .single(result) } ).start(next: { peer, remoteMessages in - guard let peer, let peerReference = PeerReference(peer._asPeer()) else { + guard let peer, let peerReference = PeerReference(peer) else { return } var messageMap: [MessageId: Message] = [:] diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index a28c15acdc..d48af232b5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -1115,7 +1115,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { inlineMedia.setSignal(updateInlineImageSignal) } case let .peerAvatar(peer): - if let peerReference = PeerReference(peer._asPeer()) { + if let peerReference = PeerReference(peer) { if let signal = peerAvatarImage(account: context.account, peerReference: peerReference, authorOfMessage: nil, representation: peer.largeProfileImage, displayDimensions: inlineMediaSize, clipStyle: .none, blurred: false, inset: 0.0, emptyColor: mainColor.withMultipliedAlpha(0.1), synchronousLoad: synchronousLoads, provideUnrounded: false) { let updateInlineImageSignal = signal |> map { images -> (TransformImageArguments) -> DrawingContext? in let image = images?.0 diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index 70ac211208..4da9e77586 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -703,7 +703,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if let updatedFile = updatedFile, updatedMedia { if let resource = updatedFile.previewRepresentations.first?.resource { - strongSelf.fetchedThumbnailDisposable.set(fetchedMediaResource(mediaBox: item.context.account.postbox.mediaBox, userLocation: .peer(item.message.id.peerId), userContentType: .video, reference: FileMediaReference.message(message: MessageReference(item.message), media: updatedFile).resourceReference(resource)).startStrict()) + strongSelf.fetchedThumbnailDisposable.set(item.context.engine.resources.fetch(reference: FileMediaReference.message(message: MessageReference(item.message), media: updatedFile).resourceReference(resource), userLocation: .peer(item.message.id.peerId), userContentType: .video).startStrict()) } else { strongSelf.fetchedThumbnailDisposable.set(nil) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index ccc39814a1..28097ae4cb 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -1471,7 +1471,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr } }, cancel: { if file.isAnimated { - context.account.postbox.mediaBox.cancelInteractiveResourceFetch(file.resource) + context.engine.resources.cancelInteractiveResourceFetch(id: EngineMediaResource.Id(file.resource.id)) } else { messageMediaFileCancelInteractiveFetch(context: context, messageId: message.id, file: file) } @@ -1706,7 +1706,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr } }, cancel: { if file.isAnimated { - context.account.postbox.mediaBox.cancelInteractiveResourceFetch(file.resource) + context.engine.resources.cancelInteractiveResourceFetch(id: EngineMediaResource.Id(file.resource.id)) } else { messageMediaFileCancelInteractiveFetch(context: context, messageId: message.id, file: file) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift index 0455a1c4a5..d769f7231b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift @@ -777,7 +777,7 @@ private final class ChannelItemComponent: Component { self.mergedAvatarsNode = mergedAvatarsNode } - mergedAvatarsNode.update(context: component.context, peers: component.peers.map { $0._asPeer() }, synchronousLoad: false, imageSize: 60.0, imageSpacing: 10.0, borderWidth: 2.0, avatarFontSize: 26.0) + mergedAvatarsNode.update(context: component.context, peers: component.peers, synchronousLoad: false, imageSize: 60.0, imageSpacing: 10.0, borderWidth: 2.0, avatarFontSize: 26.0) let avatarsSize = CGSize(width: avatarSize.width + 20.0, height: avatarSize.height) mergedAvatarsNode.updateLayout(size: avatarsSize) mergedAvatarsNode.frame = CGRect(origin: CGPoint(x: avatarFrame.midX - avatarsSize.width / 2.0, y: avatarFrame.minY), size: avatarsSize) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index dc1f534a6f..817dfb74ca 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -854,9 +854,9 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { // } let rightInset: CGFloat = 10.0 + mediaInset - let recentVoterPeers: [Peer] + let recentVoterPeers: [EnginePeer] if let optionResult { - recentVoterPeers = optionResult.recentVoterPeerIds.compactMap { message.peers[$0] } + recentVoterPeers = optionResult.recentVoterPeerIds.compactMap { message.peers[$0] }.map(EnginePeer.init) } else { recentVoterPeers = [] } @@ -2567,11 +2567,11 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets)) let typeText: String - var avatarPeers: [Peer] = [] + var avatarPeers: [EnginePeer] = [] if let poll = poll { for peerId in poll.results.recentVoters { if let peer = item.message.peers[peerId] { - avatarPeers.append(peer) + avatarPeers.append(EnginePeer(peer)) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift index 6fdde10cf3..b9efa55c34 100644 --- a/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift @@ -519,7 +519,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { animatedStickerNode.autoplay = true animatedStickerNode.visibility = strongSelf.visibilityStatus - strongSelf.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: item.context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource)).startStrict()) + strongSelf.stickerFetchedDisposable.set(item.context.engine.resources.fetch(reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource), userLocation: .other, userContentType: .sticker).startStrict()) let thumbnailDimensions = PixelDimensions(width: 512, height: 512) strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: file.immediateThumbnailData, size: emojiFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: thumbnailDimensions.cgSize) @@ -571,7 +571,7 @@ public final class ChatQrCodeScreenImpl: ViewController, ChatQrCodeScreen { public static let themeCrossfadeDelay: Double = 0.05 public enum Subject { - case peer(peer: Peer, threadId: Int64?, temporary: Bool) + case peer(peer: EnginePeer, threadId: Int64?, temporary: Bool) case messages([Message]) public var fileName: String { @@ -580,7 +580,7 @@ public final class ChatQrCodeScreenImpl: ViewController, ChatQrCodeScreen { var result: String if let addressName = peer.addressName, !addressName.isEmpty { result = "t_me-\(peer.addressName ?? "")" - } else if let peer = peer as? TelegramUser { + } else if case let .user(peer) = peer { result = "t_me-\(peer.phone ?? "")" } else { result = "t_me-\(Int32.random(in: 0 ..< Int32.max))" @@ -1576,7 +1576,7 @@ private protocol ContentNode: ASDisplayNode { private class QrContentNode: ASDisplayNode, ContentNode { private let context: AccountContext - private let peer: Peer + private let peer: EnginePeer private let threadId: Int64? private let isStatic: Bool private let temporary: Bool @@ -1618,7 +1618,7 @@ private class QrContentNode: ASDisplayNode, ContentNode { private var tokenUpdated = false var requestNextToken: () -> Void = {} - init(context: AccountContext, peer: Peer, threadId: Int64?, isStatic: Bool = false, temporary: Bool) { + init(context: AccountContext, peer: EnginePeer, threadId: Int64?, isStatic: Bool = false, temporary: Bool) { self.context = context self.peer = peer self.threadId = threadId @@ -1706,7 +1706,7 @@ private class QrContentNode: ASDisplayNode, ContentNode { self.avatarNode = ImageNode() self.avatarNode.displaysAsynchronously = false - self.avatarNode.setSignal(peerAvatarCompleteImage(account: context.account, peer: EnginePeer(peer), size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true)) + self.avatarNode.setSignal(peerAvatarCompleteImage(account: context.account, peer: peer, size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true)) super.init() @@ -1739,9 +1739,9 @@ private class QrContentNode: ASDisplayNode, ContentNode { var codeLink: String if let addressName = peer.addressName, !addressName.isEmpty { codeLink = "https://t.me/\(peer.addressName ?? "")" - } else if let peer = peer as? TelegramUser { + } else if case let .user(peer) = peer { codeLink = "https://t.me/+\(peer.phone ?? "")" - } else if let _ = peer as? TelegramChannel { + } else if case .channel = peer { codeLink = "https://t.me/c/\(peer.id.id._internalGetInt64Value())" } else { codeLink = "" diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift index f04c49c5da..b315078b7e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift @@ -20,7 +20,7 @@ public final class ChatRecentActionsController: TelegramBaseController { } private let context: AccountContext - private let peer: Peer + private let peer: EnginePeer private let initialAdminPeerId: PeerId? let starsState: StarsRevenueStats? @@ -39,7 +39,7 @@ public final class ChatRecentActionsController: TelegramBaseController { private var adminsDisposable: Disposable? - public init(context: AccountContext, peer: Peer, adminPeerId: PeerId?, starsState: StarsRevenueStats?) { + public init(context: AccountContext, peer: EnginePeer, adminPeerId: PeerId?, starsState: StarsRevenueStats?) { self.context = context self.peer = peer self.initialAdminPeerId = adminPeerId @@ -197,7 +197,7 @@ public final class ChatRecentActionsController: TelegramBaseController { let rightBarButton = ChatNavigationButton(action: .search(hasTags: false), buttonItem: UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.activateSearch))) self.rightBarButton = rightBarButton - self.titleView.title = CounterControllerTitle(title: EnginePeer(peer).compactDisplayTitle, counter: self.presentationData.strings.Channel_AdminLog_TitleAllEvents) + self.titleView.title = CounterControllerTitle(title: peer.compactDisplayTitle, counter: self.presentationData.strings.Channel_AdminLog_TitleAllEvents) let chatTheme = self.context.account.postbox.peerView(id: peer.id) |> map { view -> ChatTheme? in @@ -274,7 +274,7 @@ public final class ChatRecentActionsController: TelegramBaseController { } override public func loadDisplayNode() { - self.displayNode = ChatRecentActionsControllerNode(context: self.context, controller: self, peer: self.peer, presentationData: self.presentationData, pushController: { [weak self] c in + self.displayNode = ChatRecentActionsControllerNode(context: self.context, controller: self, peer: self.peer._asPeer(), presentationData: self.presentationData, pushController: { [weak self] c in (self?.navigationController as? NavigationController)?.pushViewController(c) }, presentController: { [weak self] c, t, a in self?.present(c, in: t, with: a, blockInteraction: true) @@ -361,7 +361,7 @@ public final class ChatRecentActionsController: TelegramBaseController { } let controller = RecentActionsSettingsSheet( context: self.context, - peer: EnginePeer(self.peer), + peer: self.peer, adminPeers: adminPeers, initialValue: RecentActionsSettingsSheet.Value( events: self.controllerNode.filter.events, @@ -380,7 +380,7 @@ public final class ChatRecentActionsController: TelegramBaseController { } private func updateTitle() { - let title = EnginePeer(self.peer).compactDisplayTitle + let title = self.peer.compactDisplayTitle let subtitle: String if self.controllerNode.filter.isEmpty { subtitle = self.presentationData.strings.Channel_AdminLog_TitleAllEvents diff --git a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift index 3b80c51c1f..8a664f562a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift @@ -114,7 +114,7 @@ public final class ChatUserInfoItemNode: ListViewItemNode, ASGestureRecognizerDe private var groupsInCommonContext: GroupsInCommonContext? private var groupsInCommonDisposable: Disposable? - private var groupsInCommon: [Peer] = [] + private var groupsInCommon: [EnginePeer] = [] private let disclaimerTextNode: TextNodeWithEntities @@ -436,7 +436,7 @@ public final class ChatUserInfoItemNode: ListViewItemNode, ASGestureRecognizerDe guard let self, let item = self.item else { return } - self.groupsInCommon = Array(state.peers.compactMap { $0.peer }.prefix(3)) + self.groupsInCommon = Array(state.peers.compactMap { $0.peer }.prefix(3)).map(EnginePeer.init) self.groupsAvatarsNode.update(context: item.context, peers: self.groupsInCommon, synchronousLoad: true, imageSize: avatarImageSize, imageSpacing: avatarSpacing, borderWidth: avatarBorder) }) } diff --git a/submodules/TelegramUI/Components/Chat/MergedAvatarsNode/Sources/MergedAvatarsNode.swift b/submodules/TelegramUI/Components/Chat/MergedAvatarsNode/Sources/MergedAvatarsNode.swift index 111a1d8640..3aa48b63ad 100644 --- a/submodules/TelegramUI/Components/Chat/MergedAvatarsNode/Sources/MergedAvatarsNode.swift +++ b/submodules/TelegramUI/Components/Chat/MergedAvatarsNode/Sources/MergedAvatarsNode.swift @@ -24,7 +24,7 @@ private enum PeerAvatarReference: Equatable { } private extension PeerAvatarReference { - init(peer: Peer) { + init(peer: EnginePeer) { if let photo = peer.smallProfileImage, let peerReference = PeerReference(peer) { self = .image(peerReference, photo) } else { @@ -97,7 +97,7 @@ public final class MergedAvatarsNode: ASDisplayNode { self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) } - public func update(context: AccountContext, peers: [Peer], synchronousLoad: Bool, imageSize: CGFloat, imageSpacing: CGFloat, borderWidth: CGFloat, avatarFontSize: CGFloat = 8.0) { + public func update(context: AccountContext, peers: [EnginePeer], synchronousLoad: Bool, imageSize: CGFloat, imageSpacing: CGFloat, borderWidth: CGFloat, avatarFontSize: CGFloat = 8.0) { self.imageSize = imageSize self.imageSpacing = imageSpacing self.borderWidthValue = borderWidth diff --git a/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift index 7c7cb283e4..717b9b56a1 100644 --- a/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift +++ b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift @@ -358,7 +358,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { strongSelf.avatarsNode = avatarsNode } let avatarSize = CGSize(width: 30.0, height: 30.0) - avatarsNode.update(context: item.context, peers: avatarPeers.map { $0._asPeer() }, synchronousLoad: false, imageSize: avatarSize.width, imageSpacing: 16.0, borderWidth: 2.0 - UIScreenPixel, avatarFontSize: 10.0) + avatarsNode.update(context: item.context, peers: avatarPeers, synchronousLoad: false, imageSize: avatarSize.width, imageSpacing: 16.0, borderWidth: 2.0 - UIScreenPixel, avatarFontSize: 10.0) let avatarsSize = CGSize(width: avatarSize.width + 16.0 * CGFloat(avatarPeers.count - 1), height: avatarSize.height) avatarsNode.updateLayout(size: avatarsSize) avatarsNode.frame = CGRect(origin: CGPoint(x: sideInset - 6.0, y: floor((layout.size.height - avatarsSize.height) / 2.0)), size: avatarsSize) diff --git a/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift index edd532154e..1eab70ac52 100644 --- a/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift +++ b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import Display import AsyncDisplayKit -import Postbox import TelegramCore import SwiftSignalKit import AccountContext @@ -500,7 +499,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { animatedStickerNode.autoplay = true animatedStickerNode.visibility = strongSelf.visibilityStatus - strongSelf.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: item.context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource)).startStrict()) + strongSelf.stickerFetchedDisposable.set(item.context.engine.resources.fetch(reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource), userLocation: .other, userContentType: .sticker).startStrict()) let thumbnailDimensions = PixelDimensions(width: 512, height: 512) strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: file.immediateThumbnailData, size: emojiFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: thumbnailDimensions.cgSize) diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerification.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerification.swift index e07f10ce92..2745c356db 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerification.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerification.swift @@ -73,7 +73,7 @@ public func ageVerificationAvailability(context: AccountContext) -> Signal take(1) |> mapToSignal { maybeFileAndMessage -> Signal in if let (file, message) = maybeFileAndMessage { - let fetchedData = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .file, reference: FileMediaReference.message(message: MessageReference(message._asMessage()), media: file).resourceReference(file.resource)) + let fetchedData = context.engine.resources.fetch(reference: FileMediaReference.message(message: MessageReference(message._asMessage()), media: file).resourceReference(file.resource), userLocation: .other, userContentType: .file) enum FetchStatus { case completed(String) diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift index 431f4cd4c1..b9d0243ea6 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift @@ -28,7 +28,7 @@ public func requireAgeVerification(context: AccountContext) -> Bool { } public func requireAgeVerification(context: AccountContext, peer: EnginePeer) -> Signal { - if requireAgeVerification(context: context), peer._asPeer().hasSensitiveContent(platform: "ios") { + if requireAgeVerification(context: context), peer.hasSensitiveContent(platform: "ios") { return context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.ContentSettings()) |> map { contentSettings in if !contentSettings.ignoreContentRestrictionReasons.contains("sensitive") { diff --git a/submodules/TelegramUI/Components/MediaEditor/ImageObjectSeparation/Sources/ImageObjectSeparation.swift b/submodules/TelegramUI/Components/MediaEditor/ImageObjectSeparation/Sources/ImageObjectSeparation.swift index b4b73cb1fd..6a91bf131a 100644 --- a/submodules/TelegramUI/Components/MediaEditor/ImageObjectSeparation/Sources/ImageObjectSeparation.swift +++ b/submodules/TelegramUI/Components/MediaEditor/ImageObjectSeparation/Sources/ImageObjectSeparation.swift @@ -65,7 +65,7 @@ public func cutoutAvailability(context: AccountContext) -> Signal take(1) |> mapToSignal { maybeFileAndMessage -> Signal in if let (file, message) = maybeFileAndMessage { - let fetchedData = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .file, reference: FileMediaReference.message(message: MessageReference(message._asMessage()), media: file).resourceReference(file.resource)) + let fetchedData = context.engine.resources.fetch(reference: FileMediaReference.message(message: MessageReference(message._asMessage()), media: file).resourceReference(file.resource), userLocation: .other, userContentType: .file) enum FetchStatus { case completed(String) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/EditStories.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/EditStories.swift index 24a1b9a5af..2f2b0b57ea 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/EditStories.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/EditStories.swift @@ -24,7 +24,7 @@ public extension MediaEditorScreenImpl { willDismiss: @escaping () -> Void = {}, update: @escaping (Disposable?) -> Void ) -> MediaEditorScreenImpl? { - guard let peerReference = PeerReference(peer._asPeer()) else { + guard let peerReference = PeerReference(peer) else { return nil } let subject: Signal diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift index 06d3b681fe..141796f01a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift @@ -71,7 +71,7 @@ public final class PeerInfoCoverComponent: Component { func colors(context: AccountContext, isDark: Bool) -> (UIColor, UIColor)? { switch self { case let .peer(peer): - if let colors = peer._asPeer().profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: isDark) }) { + if let colors = peer.profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: isDark) }) { let backgroundColor = colors.main let secondaryBackgroundColor = colors.secondary ?? colors.main return (backgroundColor, secondaryBackgroundColor) @@ -79,7 +79,7 @@ public final class PeerInfoCoverComponent: Component { return nil } case let .managedBot(peer): - if let color = peer._asPeer().nameColor { + if let color = peer.nameColor { let colors = calculateAvatarColors(context: context, explicitColorIndex: nil, peerId: peer.id, nameColor: color, icon: .none, theme: nil) if colors.count == 2 { return (colors[0], colors[1]) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift index e9a40d6257..3df2239c71 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenMemberItem.swift @@ -20,7 +20,7 @@ enum PeerInfoScreenMemberItemAction { final class PeerInfoScreenMemberItem: PeerInfoScreenItem { let id: AnyHashable let context: ItemListPeerItem.Context - let enclosingPeer: Peer? + let enclosingPeer: EnginePeer? let member: PeerInfoMember let badge: String? let isAccount: Bool @@ -31,7 +31,7 @@ final class PeerInfoScreenMemberItem: PeerInfoScreenItem { init( id: AnyHashable, context: ItemListPeerItem.Context, - enclosingPeer: Peer?, + enclosingPeer: EnginePeer?, member: PeerInfoMember, badge: String? = nil, isAccount: Bool, @@ -149,9 +149,9 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { case .member: var canEditRank = false if item.member.id == item.context.accountPeerId { - if let channel = item.enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank) { + if case let .channel(channel) = item.enclosingPeer, channel.hasPermission(.editRank) { canEditRank = true - } else if let group = item.enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { + } else if case let .legacyGroup(group) = item.enclosingPeer, !group.hasBannedPermission(.banEditRank) { canEditRank = true } } @@ -175,16 +175,16 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { break } - let actions = availableActionsForMemberOfPeer(accountPeerId: item.context.accountPeerId, peer: item.enclosingPeer.flatMap(EnginePeer.init), member: item.member) + let actions = availableActionsForMemberOfPeer(accountPeerId: item.context.accountPeerId, peer: item.enclosingPeer, member: item.member) var options: [ItemListPeerItemRevealOption] = [] - if actions.contains(.promote) && item.enclosingPeer is TelegramChannel { + if actions.contains(.promote), case .channel = item.enclosingPeer { options.append(ItemListPeerItemRevealOption(type: .neutral, title: presentationData.strings.GroupInfo_ActionPromote, action: { item.action?(.promote) })) } if actions.contains(.restrict) { - if item.enclosingPeer is TelegramChannel { + if case .channel = item.enclosingPeer { options.append(ItemListPeerItemRevealOption(type: .warning, title: presentationData.strings.GroupInfo_ActionRestrict, action: { item.action?(.restrict) })) @@ -220,7 +220,7 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { itemText = .presence } - let peerItem = ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), systemStyle: .glass, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: item.context, peer: EnginePeer(item.member.peer), height: itemHeight, presence: item.member.presence.flatMap(EnginePeer.Presence.init), text: itemText, label: itemLabel, editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: nil), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: false, animateFirstAvatarTransition: !item.isAccount, sectionId: 0, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in + let peerItem = ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), systemStyle: .glass, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: item.context, peer: item.member.peer, height: itemHeight, presence: item.member.presence.flatMap(EnginePeer.Presence.init), text: itemText, label: itemLabel, editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: nil), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: false, animateFirstAvatarTransition: !item.isAccount, sectionId: 0, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in }, removePeer: { _ in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift index b1600c7312..483ae0b683 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -25,23 +25,23 @@ private struct GroupsInCommonListTransaction { private struct GroupsInCommonListEntry: Comparable, Identifiable { var index: Int - var peer: Peer - + var peer: EnginePeer + var stableId: PeerId { return self.peer.id } - + static func ==(lhs: GroupsInCommonListEntry, rhs: GroupsInCommonListEntry) -> Bool { - return lhs.peer.isEqual(rhs.peer) + return lhs.peer == rhs.peer } static func <(lhs: GroupsInCommonListEntry, rhs: GroupsInCommonListEntry) -> Bool { return lhs.index < rhs.index } - func item(context: AccountContext, presentationData: PresentationData, openPeer: @escaping (Peer) -> Void, openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) -> ListViewItem { + func item(context: AccountContext, presentationData: PresentationData, openPeer: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void) -> ListViewItem { let peer = self.peer - return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: EnginePeer(self.peer), presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, selectable: true, sectionId: 0, action: { + return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: self.peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, selectable: true, sectionId: 0, action: { openPeer(peer) }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in @@ -51,7 +51,7 @@ private struct GroupsInCommonListEntry: Comparable, Identifiable { } } -private func preparedTransition(from fromEntries: [GroupsInCommonListEntry], to toEntries: [GroupsInCommonListEntry], context: AccountContext, presentationData: PresentationData, openPeer: @escaping (Peer) -> Void, openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) -> GroupsInCommonListTransaction { +private func preparedTransition(from fromEntries: [GroupsInCommonListEntry], to toEntries: [GroupsInCommonListEntry], context: AccountContext, presentationData: PresentationData, openPeer: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void) -> GroupsInCommonListTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } @@ -65,7 +65,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { private let context: AccountContext private let peerId: PeerId private let chatControllerInteraction: ChatControllerInteraction - private let openPeerContextAction: (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void + private let openPeerContextAction: (Bool, EnginePeer, ASDisplayNode, ContextGesture?) -> Void private let groupsInCommonContext: GroupsInCommonContext weak var parentController: ViewController? @@ -106,7 +106,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { private var disposable: Disposable? - init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void, groupsInCommonContext: GroupsInCommonContext) { + init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, EnginePeer, ASDisplayNode, ContextGesture?) -> Void, groupsInCommonContext: GroupsInCommonContext) { self.context = context self.peerId = peerId self.chatControllerInteraction = chatControllerInteraction @@ -229,11 +229,11 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { var entries: [GroupsInCommonListEntry] = [] for peer in state.peers { if let peer = peer.peer { - entries.append(GroupsInCommonListEntry(index: entries.count, peer: peer)) + entries.append(GroupsInCommonListEntry(index: entries.count, peer: EnginePeer(peer))) } } let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, openPeer: { [weak self] peer in - self?.chatControllerInteraction.openPeer(EnginePeer(peer), .default, nil, .default) + self?.chatControllerInteraction.openPeer(peer, .default, nil, .default) }, openPeerContextAction: { [weak self] peer, node, gesture in self?.openPeerContextAction(false, peer, node, gesture) }) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift index 00630d07d8..240e6fb2f4 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift @@ -89,7 +89,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { } } - func item(context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem { + func item(context: AccountContext, presentationData: PresentationData, enclosingPeer: EnginePeer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem { switch self { case let .addMember(_, text): return ItemListPeerActionItem(presentationData: ItemListPresentationData(presentationData), icon: PresentationResourcesItemList.addPersonIcon(presentationData.theme), title: text, alwaysPlain: true, sectionId: 0, height: .compactPeerList, color: .accent, editing: false, action: { @@ -110,9 +110,9 @@ private enum PeerMembersListEntry: Comparable, Identifiable { case .member: var canEditRank = false if member.id == context.account.peerId { - if let channel = enclosingPeer as? TelegramChannel, channel.hasPermission(.editRank) { + if case let .channel(channel) = enclosingPeer, channel.hasPermission(.editRank) { canEditRank = true - } else if let group = enclosingPeer as? TelegramGroup, !group.hasBannedPermission(.banEditRank) { + } else if case let .legacyGroup(group) = enclosingPeer, !group.hasBannedPermission(.banEditRank) { canEditRank = true } } @@ -136,16 +136,16 @@ private enum PeerMembersListEntry: Comparable, Identifiable { break } - let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: EnginePeer(enclosingPeer), member: member) + let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: enclosingPeer, member: member) var options: [ItemListPeerItemRevealOption] = [] - if actions.contains(.promote) && enclosingPeer is TelegramChannel { + if actions.contains(.promote), case .channel = enclosingPeer { options.append(ItemListPeerItemRevealOption(type: .neutral, title: presentationData.strings.GroupInfo_ActionPromote, action: { action(member, .promote) })) } if actions.contains(.restrict) { - if enclosingPeer is TelegramChannel { + if case .channel = enclosingPeer { options.append(ItemListPeerItemRevealOption(type: .warning, title: presentationData.strings.GroupInfo_ActionRestrict, action: { action(member, .restrict) })) @@ -165,7 +165,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { } var status: ContactsPeerItemStatus = .presence(presence, presentationData.dateTimeFormat) - if let user = member.peer as? TelegramUser, let botInfo = user.botInfo { + if case let .user(user) = member.peer, let botInfo = user.botInfo { let botStatus: String if botInfo.flags.contains(.hasAccessToChatHistory) { botStatus = presentationData.strings.Bot_GroupStatusReadsHistory @@ -188,7 +188,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { displayOrder: presentationData.nameDisplayOrder, context: context, peerMode: .memberList, - peer: .peer(peer: EnginePeer(member.peer), chatPeer: EnginePeer(member.peer)), + peer: .peer(peer: member.peer, chatPeer: member.peer), status: status, rightLabelText: label.flatMap { .init(text: $0, color: labelColor, hasBackground: labelBackground) }, enabled: true, @@ -268,7 +268,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { } } -private func preparedTransition(from fromEntries: [PeerMembersListEntry], to toEntries: [PeerMembersListEntry], context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> PeerMembersListTransaction { +private func preparedTransition(from fromEntries: [PeerMembersListEntry], to toEntries: [PeerMembersListEntry], context: AccountContext, presentationData: PresentationData, enclosingPeer: EnginePeer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> PeerMembersListTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } @@ -290,7 +290,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { private let listMaskView: UIImageView private let listNode: ListView private var currentEntries: [PeerMembersListEntry] = [] - private var enclosingPeer: Peer? + private var enclosingPeer: EnginePeer? private var currentState: PeerInfoMembersState? private var canLoadMore: Bool = false private var enqueuedTransactions: [PeerMembersListTransaction] = [] @@ -358,9 +358,9 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { return } - strongSelf.enclosingPeer = enclosingPeer._asPeer() + strongSelf.enclosingPeer = enclosingPeer strongSelf.currentState = state - strongSelf.updateState(enclosingPeer: enclosingPeer._asPeer(), state: state, presentationData: presentationData) + strongSelf.updateState(enclosingPeer: enclosingPeer, state: state, presentationData: presentationData) }) self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in @@ -439,7 +439,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { } } - private func updateState(enclosingPeer: Peer, state: PeerInfoMembersState, presentationData: PresentationData) { + private func updateState(enclosingPeer: EnginePeer, state: PeerInfoMembersState, presentationData: PresentationData) { var entries: [PeerMembersListEntry] = [] if state.canAddMembers { entries.append(.addMember(presentationData.theme, presentationData.strings.GroupInfo_AddParticipant)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedPeersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedPeersPane.swift index 31e13a2fd3..4bb5725e15 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedPeersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedPeersPane.swift @@ -65,7 +65,7 @@ private enum RecommendedPeersListEntry: Comparable, Identifiable { } } - func item(context: AccountContext, presentationData: PresentationData, action: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) -> ListViewItem { + func item(context: AccountContext, presentationData: PresentationData, action: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void) -> ListViewItem { switch self { case let .peer(_, _, peer, subscribers): let text: ItemListPeerItemText @@ -85,13 +85,13 @@ private enum RecommendedPeersListEntry: Comparable, Identifiable { }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, contextAction: { node, gesture in - openPeerContextAction(peer._asPeer(), node, gesture) + openPeerContextAction(peer, node, gesture) }, hasTopStripe: false, noInsets: true, noCorners: true, style: .plain, disableInteractiveTransitionIfNecessary: true) } } } -private func preparedTransition(from fromEntries: [RecommendedPeersListEntry], to toEntries: [RecommendedPeersListEntry], context: AccountContext, presentationData: PresentationData, action: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) -> RecommendedPeersListTransaction { +private func preparedTransition(from fromEntries: [RecommendedPeersListEntry], to toEntries: [RecommendedPeersListEntry], context: AccountContext, presentationData: PresentationData, action: @escaping (EnginePeer) -> Void, openPeerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void) -> RecommendedPeersListTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } @@ -116,7 +116,7 @@ extension RecommendedBots: RecommendedPeers { final class PeerInfoRecommendedPeersPaneNode: ASDisplayNode, PeerInfoPaneNode { private let context: AccountContext private let chatControllerInteraction: ChatControllerInteraction - private let openPeerContextAction: (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void + private let openPeerContextAction: (Bool, EnginePeer, ASDisplayNode, ContextGesture?) -> Void weak var parentController: ViewController? @@ -152,7 +152,7 @@ final class PeerInfoRecommendedPeersPaneNode: ASDisplayNode, PeerInfoPaneNode { private var disposable: Disposable? - init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void) { + init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, EnginePeer, ASDisplayNode, ContextGesture?) -> Void) { self.context = context self.chatControllerInteraction = chatControllerInteraction self.openPeerContextAction = openPeerContextAction diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift index 00af60cf77..aa6d9242e3 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarListNode.swift @@ -23,7 +23,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode { let isReady = Promise() - var arguments: (Peer?, Int64?, EngineMessageHistoryThread.Info?, PresentationTheme, CGFloat, Bool)? + var arguments: (EnginePeer?, Int64?, EngineMessageHistoryThread.Info?, PresentationTheme, CGFloat, Bool)? var item: PeerInfoAvatarListItem? var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)? @@ -128,7 +128,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode { } } - func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, isForum: Bool, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: EnginePeer?, isForum: Bool, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded) self.maskNode.isForum = isForum self.pinchSourceNode.update(size: size, transition: transition) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift index aaf95233c6..7a99f8ac79 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoAvatarTransformContainerNode.swift @@ -101,7 +101,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { self.playbackStartDisposable.dispose() } - func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme, peer: Peer?) { + func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme, peer: EnginePeer?) { var colors = AvatarNode.Colors(theme: theme) let regularNavigationContentsSecondaryColor: UIColor @@ -160,7 +160,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { } var isForum = false - if let peer, let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { + if let peer, case let .channel(channel) = peer, channel.isForumOrMonoForum { isForum = true } @@ -210,7 +210,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { } private struct Params { - let peer: Peer? + let peer: EnginePeer? let threadId: Int64? let threadInfo: EngineMessageHistoryThread.Info? let item: PeerInfoAvatarListItem? @@ -219,7 +219,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { let isExpanded: Bool let isSettings: Bool - init(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) { + init(peer: EnginePeer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) { self.peer = peer self.threadId = threadId self.threadInfo = threadInfo @@ -251,7 +251,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { ) } - func update(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) { + func update(peer: EnginePeer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) { self.params = Params(peer: peer, threadId: threadId, threadInfo: threadInfo, item: item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: isSettings) if let peer = peer { @@ -282,7 +282,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { } self.avatarNode.imageNode.animateFirstTransition = !isSettings - self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true) + self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, clipStyle: .none, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true) if let threadInfo = threadInfo { self.avatarNode.isHidden = true @@ -327,7 +327,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { var isForum = false let avatarCornerRadius: CGFloat - if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { + if case let .channel(channel) = peer, channel.isForumOrMonoForum { avatarCornerRadius = floor(avatarSize * 0.25) isForum = true } else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 531c31ceee..dea0dfbc1c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -384,8 +384,8 @@ final class PeerInfoPersonalChannelData: Equatable { final class PeerInfoScreenData { let peer: EnginePeer? - let chatPeer: Peer? - let savedMessagesPeer: Peer? + let chatPeer: EnginePeer? + let savedMessagesPeer: EnginePeer? let cachedData: CachedPeerData? let status: PeerInfoStatusData? let peerNotificationSettings: TelegramPeerNotificationSettings? @@ -393,8 +393,8 @@ final class PeerInfoScreenData { let globalNotificationSettings: EngineGlobalNotificationSettings? let availablePanes: [PeerInfoPaneKey] let groupsInCommon: GroupsInCommonContext? - let linkedDiscussionPeer: Peer? - let linkedMonoforumPeer: Peer? + let linkedDiscussionPeer: EnginePeer? + let linkedMonoforumPeer: EnginePeer? let members: PeerInfoMembersData? let storyListContext: StoryListContext? let storyArchiveListContext: StoryListContext? @@ -440,8 +440,8 @@ final class PeerInfoScreenData { init( peer: EnginePeer?, - chatPeer: Peer?, - savedMessagesPeer: Peer?, + chatPeer: EnginePeer?, + savedMessagesPeer: EnginePeer?, cachedData: CachedPeerData?, status: PeerInfoStatusData?, peerNotificationSettings: TelegramPeerNotificationSettings?, @@ -450,8 +450,8 @@ final class PeerInfoScreenData { isContact: Bool, availablePanes: [PeerInfoPaneKey], groupsInCommon: GroupsInCommonContext?, - linkedDiscussionPeer: Peer?, - linkedMonoforumPeer: Peer?, + linkedDiscussionPeer: EnginePeer?, + linkedMonoforumPeer: EnginePeer?, members: PeerInfoMembersData?, storyListContext: StoryListContext?, storyArchiveListContext: StoryListContext?, @@ -1025,7 +1025,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, return PeerInfoScreenData( peer: peer.flatMap(EnginePeer.init), - chatPeer: peer, + chatPeer: peer.flatMap(EnginePeer.init), savedMessagesPeer: nil, cachedData: peerView.cachedData, status: nil, @@ -1618,8 +1618,8 @@ func peerInfoScreenData( return PeerInfoScreenData( peer: peer.flatMap(EnginePeer.init), - chatPeer: peerView.peers[peerId], - savedMessagesPeer: savedMessagesPeer?._asPeer(), + chatPeer: peerView.peers[peerId].flatMap(EnginePeer.init), + savedMessagesPeer: savedMessagesPeer, cachedData: peerView.cachedData, status: status, peerNotificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings, @@ -1833,14 +1833,14 @@ func peerInfoScreenData( } } - var discussionPeer: Peer? + var discussionPeer: EnginePeer? if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] { - discussionPeer = peer + discussionPeer = EnginePeer(peer) } - - var monoforumPeer: Peer? + + var monoforumPeer: EnginePeer? if let channel = peerViewMainPeer(peerView) as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), let linkedMonoforumId = channel.linkedMonoforumId { - monoforumPeer = peerView.peers[linkedMonoforumId] + monoforumPeer = peerView.peers[linkedMonoforumId].flatMap(EnginePeer.init) } var canManageInvitations = false @@ -1865,7 +1865,7 @@ func peerInfoScreenData( return PeerInfoScreenData( peer: peerView.peers[peerId].flatMap(EnginePeer.init), - chatPeer: peerView.peers[peerId], + chatPeer: peerView.peers[peerId].flatMap(EnginePeer.init), savedMessagesPeer: nil, cachedData: peerView.cachedData, status: status, @@ -2128,14 +2128,14 @@ func peerInfoScreenData( starsRevenueContextAndState ) |> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, threadData, preferencesView, accountIsPremium, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting, starsRevenueContextAndState -> Signal in - var discussionPeer: Peer? + var discussionPeer: EnginePeer? if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] { - discussionPeer = peer + discussionPeer = EnginePeer(peer) } - - var monoforumPeer: Peer? + + var monoforumPeer: EnginePeer? if let channel = peerViewMainPeer(peerView) as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), let linkedMonoforumId = channel.linkedMonoforumId { - monoforumPeer = peerView.peers[linkedMonoforumId] + monoforumPeer = peerView.peers[linkedMonoforumId].flatMap(EnginePeer.init) } var availablePanes = availablePanes @@ -2203,7 +2203,7 @@ func peerInfoScreenData( return .single(PeerInfoScreenData( peer: peerView.peers[groupId].flatMap(EnginePeer.init), - chatPeer: peerView.peers[groupId], + chatPeer: peerView.peers[groupId].flatMap(EnginePeer.init), savedMessagesPeer: nil, cachedData: peerView.cachedData, status: status, @@ -2256,7 +2256,7 @@ func peerInfoIsCopyProtected(data: PeerInfoScreenData) -> Bool { var isCopyProtected = false if let cachedUserData = data.cachedData as? CachedUserData, cachedUserData.flags.contains(.copyProtectionEnabled) || cachedUserData.flags.contains(.myCopyProtectionEnabled) { isCopyProtected = true - } else if let peer = data.peer, peer._asPeer().isCopyProtectionEnabled { + } else if let peer = data.peer, peer.isCopyProtectionEnabled { isCopyProtected = true } return isCopyProtected diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift index 5e3082f093..076c092cce 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoEditingAvatarNode.swift @@ -156,14 +156,14 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode { } markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0)) markupNode.updateVisibility(true) - } else if threadData == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer._asPeer()) { + } else if threadData == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) { if let markupNode = self.markupNode { self.markupNode = nil markupNode.removeFromSupernode() } let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil, videoCodec: nil)], alternativeRepresentations: [])) - let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer._asPeer().isCopyProtectionEnabled, storeAfterDownload: nil) + let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil) if videoContent.id != self.videoContent?.id { self.videoNode?.removeFromSupernode() diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderEditingContentNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderEditingContentNode.swift index d71e96fc87..dafef8e000 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderEditingContentNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderEditingContentNode.swift @@ -112,7 +112,11 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode { } } case .lastName: - updateText = (peer?._asPeer() as? TelegramUser)?.lastName ?? "" + if case let .user(user) = peer { + updateText = user.lastName ?? "" + } else { + updateText = "" + } case .title: updateText = peer?.debugDisplayTitle ?? "" case .description: diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 952aa688a7..fdc26f3789 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -89,7 +89,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { private weak var controller: PeerInfoScreenImpl? private var presentationData: PresentationData? private var state: PeerInfoState? - private var peer: Peer? + private var peer: EnginePeer? private var threadData: MessageHistoryThreadData? private var isSearching: Bool = false private var avatarSize: CGFloat? @@ -360,7 +360,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { guard let strongSelf = self, let state = strongSelf.state, let peer = strongSelf.peer, let presentationData = strongSelf.presentationData, let avatarSize = strongSelf.avatarSize else { return } - strongSelf.editingContentNode.avatarNode.update(peer: EnginePeer(peer), threadData: strongSelf.threadData, chatLocation: chatLocation, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) + strongSelf.editingContentNode.avatarNode.update(peer: peer, threadData: strongSelf.threadData, chatLocation: chatLocation, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) } self.avatarListNode.animateOverlaysFadeIn = { [weak self] in @@ -518,7 +518,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } self.state = state - self.peer = peer?._asPeer() + self.peer = peer self.threadData = threadData self.isSearching = isSearching self.avatarListNode.listContainerNode.peer = peer @@ -1812,7 +1812,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { }) } - self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer?._asPeer(), isForum: isForum, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition) + self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, isForum: isForum, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition) self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) self.avatarOverlayNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) if additive { @@ -2051,7 +2051,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } self.controller?.push(ProfileLevelInfoScreen( context: self.context, - peer: EnginePeer(peer), + peer: peer, starRating: currentStarRating, pendingStarRating: self.currentPendingStarRating, customTheme: self.presentationData?.theme diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift index a9a1d9c92f..0f2d18d73f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift @@ -42,7 +42,7 @@ final class PeerInfoInteraction { let openPermissions: () -> Void let openLocation: () -> Void let editingOpenSetupLocation: () -> Void - let openPeerInfo: (Peer, Bool) -> Void + let openPeerInfo: (EnginePeer, Bool) -> Void let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode, CGRect?) -> Void let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void @@ -120,7 +120,7 @@ final class PeerInfoInteraction { openPermissions: @escaping () -> Void, openLocation: @escaping () -> Void, editingOpenSetupLocation: @escaping () -> Void, - openPeerInfo: @escaping (Peer, Bool) -> Void, + openPeerInfo: @escaping (EnginePeer, Bool) -> Void, performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void, openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode, CGRect?) -> Void, performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift index 6ea4ea19b3..1e504b10fb 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift @@ -27,14 +27,14 @@ enum PeerInfoMember: Equatable { } } - var peer: Peer { + var peer: EnginePeer { switch self { case let .channelMember(participant, _): - return participant.peer._asPeer() + return participant.peer case let .legacyGroupMember(peer, _, _, _, _, _): - return peer.peers[peer.peerId]! + return EnginePeer(peer.peers[peer.peerId]!) case let .account(peer): - return peer.peers[peer.peerId]! + return EnginePeer(peer.peers[peer.peerId]!) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index c70713fcf2..6496e23c80 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -408,7 +408,7 @@ private final class PeerInfoPendingPane { updatedPresentationData: (initial: PresentationData, signal: Signal)?, chatControllerInteraction: ChatControllerInteraction, data: PeerInfoScreenData, - openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void, + openPeerContextAction: @escaping (Bool, EnginePeer, ASDisplayNode, ContextGesture?) -> Void, openAddMemberAction: @escaping () -> Void, requestPerformPeerMemberAction: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, peerId: PeerId, @@ -637,7 +637,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat var selectionPanelNode: PeerInfoSelectionPanelNode? var chatControllerInteraction: ChatControllerInteraction? - var openPeerContextAction: ((Bool, Peer, ASDisplayNode, ContextGesture?) -> Void)? + var openPeerContextAction: ((Bool, EnginePeer, ASDisplayNode, ContextGesture?) -> Void)? var openAddMemberAction: (() -> Void)? var requestPerformPeerMemberAction: ((PeerInfoMember, PeerMembersListAction) -> Void)? diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift index fdb62f98be..eeb1b21f0f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift @@ -521,7 +521,7 @@ func infoItems( if let managedByBot = data.managedByBot { items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemBotAddToChatInfo, icon: .managedBot, text: presentationData.strings.PeerInfo_ManagedBotFooter(managedByBot.compactDisplayTitle).string, linkAction: { _ in - interaction.openPeerInfo(managedByBot._asPeer(), false) + interaction.openPeerInfo(managedByBot, false) })) } else { items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemBotAddToChatInfo, text: presentationData.strings.Bot_AddToChatInfo)) @@ -849,7 +849,7 @@ func infoItems( for member in memberList { let isAccountPeer = member.id == context.account.peerId - items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: .account(context), enclosingPeer: peer._asPeer(), member: member, isAccount: false, action: isAccountPeer ? { _ in + items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: .account(context), enclosingPeer: peer, member: member, isAccount: false, action: isAccountPeer ? { _ in let actions = availableActionsForMemberOfPeer(accountPeerId: context.account.peerId, peer: peer, member: member) if actions.contains(.editRank) { interaction.performMemberAction(member, .editRank) @@ -1099,7 +1099,7 @@ func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, s if let addressName = peer.addressName, !addressName.isEmpty { discussionGroupTitle = "@\(addressName)" } else { - discussionGroupTitle = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + discussionGroupTitle = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) } } else { discussionGroupTitle = presentationData.strings.Channel_DiscussionGroupAdd @@ -1194,7 +1194,7 @@ func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, s if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { let labelString: NSAttributedString if channel.linkedMonoforumId != nil { - if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel { + if case let .channel(monoforumPeer) = data.linkedMonoforumPeer { if let sendPaidMessageStars = monoforumPeer.sendPaidMessageStars { let formattedLabel = formatStarsAmountText(sendPaidMessageStars, dateTimeFormat: presentationData.dateTimeFormat) let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) @@ -1406,7 +1406,7 @@ func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, s if let addressName = linkedDiscussionPeer.addressName, !addressName.isEmpty { peerTitle = "@\(addressName)" } else { - peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + peerTitle = linkedDiscussionPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) } items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemLinkedChannel, label: .text(peerTitle), text: presentationData.strings.Group_LinkedChannel, icon: PresentationResourcesSettings.channels, action: { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 4653191d7b..2916c8f27d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -1328,7 +1328,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro items = [ .action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ImageEnlarge"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in f(.dismissWithoutContent) - self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, .default) + self?.chatInterfaceInteraction.openPeer(peer, .default, nil, .default) })), .action(ContextMenuActionItem(text: presentationData.strings.Chat_SimilarChannels_Join, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in f(.dismissWithoutContent) @@ -1336,14 +1336,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let self else { return } - self.joinChannel(peer: EnginePeer(peer)) + self.joinChannel(peer: peer) })) ] } else { items = [ .action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ImageEnlarge"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.dismissWithoutContent) - self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, .default) + self?.chatInterfaceInteraction.openPeer(peer, .default, nil, .default) })) ] } @@ -2571,7 +2571,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.headerNode.avatarListNode.avatarContainerNode.storyProgress != mappedValue { self.headerNode.avatarListNode.avatarContainerNode.storyProgress = mappedValue - self.headerNode.avatarListNode.avatarContainerNode.updateStoryView(transition: .immediate, theme: self.presentationData.theme, peer: peer?._asPeer()) + self.headerNode.avatarListNode.avatarContainerNode.updateStoryView(transition: .immediate, theme: self.presentationData.theme, peer: peer) } }) self.expiringStoryListDisposable = (combineLatest(queue: .mainQueue(), @@ -3210,7 +3210,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - func openClearHistory(contextController: ContextControllerProtocol, clearPeerHistory: ClearPeerHistory, peer: Peer, chatPeer: Peer) { + func openClearHistory(contextController: ContextControllerProtocol, clearPeerHistory: ClearPeerHistory, peer: Peer, chatPeer: EnginePeer) { var subItems: [ContextMenuItem] = [] subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Common_Back, icon: { theme in @@ -3227,9 +3227,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let title: String switch anyType { case .user, .secretChat, .savedMessages: - title = self.presentationData.strings.PeerInfo_ClearConfirmationUser(EnginePeer(chatPeer).compactDisplayTitle).string + title = self.presentationData.strings.PeerInfo_ClearConfirmationUser(chatPeer.compactDisplayTitle).string case .group, .channel: - title = self.presentationData.strings.PeerInfo_ClearConfirmationGroup(EnginePeer(chatPeer).compactDisplayTitle).string + title = self.presentationData.strings.PeerInfo_ClearConfirmationGroup(chatPeer.compactDisplayTitle).string } subItems.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in @@ -3248,7 +3248,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let text: String switch canClearForEveryone { case .user, .secretChat, .savedMessages: - text = self.presentationData.strings.Conversation_DeleteMessagesFor(EnginePeer(chatPeer).compactDisplayTitle).string + text = self.presentationData.strings.Conversation_DeleteMessagesFor(chatPeer.compactDisplayTitle).string case .channel, .group: text = self.presentationData.strings.Conversation_DeleteMessagesForEveryone } @@ -3266,7 +3266,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let text: String switch canClearForMyself { case .secretChat: - text = self.presentationData.strings.Conversation_DeleteMessagesFor(EnginePeer(chatPeer).compactDisplayTitle).string + text = self.presentationData.strings.Conversation_DeleteMessagesFor(chatPeer.compactDisplayTitle).string default: text = self.presentationData.strings.Conversation_DeleteMessagesForMe } @@ -4301,9 +4301,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.controller?.push(controller) } - private func openPeerInfo(peer: Peer, isMember: Bool) { + private func openPeerInfo(peer: EnginePeer, isMember: Bool) { let mode: PeerInfoControllerMode = .generic - if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: EnginePeer(peer), mode: mode, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer, mode: mode, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { (self.controller?.navigationController as? NavigationController)?.pushViewController(infoController) } } @@ -4499,7 +4499,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro ActionSheetTextItem(title: text), ActionSheetButtonItem(title: title, color: .destructive, action: { dismissAction() - self?.deletePeerChat(peer: peer._asPeer(), globally: true) + self?.deletePeerChat(peer: peer, globally: true) }), ]), ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) @@ -4553,7 +4553,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: title, text: text, actions: [ TextAlertAction(type: .destructiveAction, title: actionText, action: { - self?.deletePeerChat(peer: peer._asPeer(), globally: delete) + self?.deletePeerChat(peer: peer, globally: delete) }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }) @@ -4561,7 +4561,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - private func deletePeerChat(peer: Peer, globally: Bool) { + private func deletePeerChat(peer: EnginePeer, globally: Bool) { guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else { return } @@ -4570,7 +4570,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: EngineRenderedPeer(peer: EnginePeer(peer)), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in + chatListController.maybeAskForPeerChatRemoval(peer: EngineRenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in if deleted { navigationController?.popToRoot(animated: true) } @@ -4603,7 +4603,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } - presentAddMembersImpl(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, parentController: controller, groupPeer: groupPeer._asPeer(), selectAddMemberDisposable: self.selectAddMemberDisposable, addMemberDisposable: self.addMemberDisposable) + presentAddMembersImpl(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, parentController: controller, groupPeer: groupPeer, selectAddMemberDisposable: self.selectAddMemberDisposable, addMemberDisposable: self.addMemberDisposable) } func openQrCode() { @@ -5396,7 +5396,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let headerInset = sectionInset - let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer.flatMap(EnginePeer.init) ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, isSearching: self.searchDisplayController != nil, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: transition.isAnimated && self.headerNode.navigationTransition == nil) + let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, isSearching: self.searchDisplayController != nil, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: transition.isAnimated && self.headerNode.navigationTransition == nil) let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: layout.size.height)) if additive { @@ -5802,7 +5802,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let headerInset = sectionInset - let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer.flatMap(EnginePeer.init) ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, isSearching: self.searchDisplayController != nil, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: animateHeader && self.headerNode.navigationTransition == nil) + let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, isSearching: self.searchDisplayController != nil, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: animateHeader && self.headerNode.navigationTransition == nil) } let paneAreaExpansionDistance: CGFloat = 32.0 @@ -6564,7 +6564,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc if let primary = primary { let size = CGSize(width: 31.0, height: 31.0) let inset: CGFloat = 3.0 - if let signal = peerAvatarImage(account: primary.0, peerReference: PeerReference(primary.1._asPeer()), authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) { + if let signal = peerAvatarImage(account: primary.0, peerReference: PeerReference(primary.1), authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) { return signal |> map { imageVersions -> (UIImage, UIImage)? in if let image = imageVersions?.0 { @@ -7413,16 +7413,16 @@ struct ClearPeerHistory { var canClearForMyself: ClearType? = nil var canClearForEveryone: ClearType? = nil - init(context: AccountContext, peer: Peer, chatPeer: Peer, cachedData: CachedPeerData?) { + init(context: AccountContext, peer: Peer, chatPeer: EnginePeer, cachedData: CachedPeerData?) { if peer.id == context.account.peerId { canClearCache = false canClearForMyself = .savedMessages canClearForEveryone = nil - } else if chatPeer is TelegramSecretChat { + } else if case .secretChat = chatPeer { canClearCache = false canClearForMyself = .secretChat canClearForEveryone = nil - } else if let group = chatPeer as? TelegramGroup { + } else if case let .legacyGroup(group) = chatPeer { canClearCache = false switch group.role { @@ -7433,7 +7433,7 @@ struct ClearPeerHistory { canClearForMyself = .group canClearForEveryone = nil } - } else if let channel = chatPeer as? TelegramChannel { + } else if case let .channel(channel) = chatPeer { var canDeleteHistory = false if let cachedData = cachedData as? CachedChannelData { canDeleteHistory = cachedData.flags.contains(.canDeleteHistory) @@ -7461,7 +7461,7 @@ struct ClearPeerHistory { canClearCache = false canClearForMyself = .user - if let user = chatPeer as? TelegramUser, user.botInfo != nil { + if case let .user(user) = chatPeer, user.botInfo != nil { canClearForEveryone = nil } else { canClearForEveryone = .user diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift index 19b6433cc4..b9fba293f5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift @@ -22,7 +22,7 @@ extension PeerInfoScreenNode { navigationController.setViewControllers(viewControllers, animated: true) current.activateSearch(domain: .everything, query: "") } else if let peer = self.data?.chatPeer { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, activateMessageSearch: (.everything, ""))) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), keepStack: .default, activateMessageSearch: (.everything, ""))) } } } @@ -86,7 +86,7 @@ extension PeerInfoScreenNode { return } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, setupController: { controller in + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), keepStack: .default, setupController: { controller in controller.beginClearHistory(type: type) })) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift index 9d401e619f..90734a0c9f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMember.swift @@ -24,7 +24,7 @@ extension PeerInfoScreenNode { guard let self, let navigationController = self.controller?.navigationController as? NavigationController else { return } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(member.peer)))) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(member.peer))) } }))) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift index 3ec89d56cb..99088bd1e9 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift @@ -158,7 +158,7 @@ extension PeerInfoScreenNode { if self.resolvePeerByNameDisposable == nil { self.resolvePeerByNameDisposable = MetaDisposable() } - var resolveSignal: Signal + var resolveSignal: Signal if let peerName = peerName { resolveSignal = self.context.engine.peers.resolvePeerByName(name: peerName, referrer: nil) |> mapToSignal { result -> Signal in @@ -167,9 +167,6 @@ extension PeerInfoScreenNode { } return .single(result) } - |> mapToSignal { peer -> Signal in - return .single(peer?._asPeer()) - } } else { resolveSignal = self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)) |> mapToSignal { peer -> Signal in @@ -179,7 +176,7 @@ extension PeerInfoScreenNode { return .never() } } - |> map { Optional($0._asPeer()) } + |> map { Optional($0) } } var cancelImpl: (() -> Void)? let presentationData = self.presentationData @@ -210,7 +207,7 @@ extension PeerInfoScreenNode { self.resolvePeerByNameDisposable?.set((resolveSignal |> deliverOnMainQueue).start(next: { [weak self] peer in if let strongSelf = self, !hashtag.isEmpty { - let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag) + let searchController = HashtagSearchController(context: strongSelf.context, peer: peer, query: hashtag) strongSelf.controller?.push(searchController) } })) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift index 67c2b0462f..a04be400a4 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift @@ -425,18 +425,18 @@ extension PeerInfoScreenNode { var canSetupAutoremoveTimeout = false - if let secretChat = chatPeer as? TelegramSecretChat { + if case let .secretChat(secretChat) = chatPeer { currentAutoremoveTimeout = secretChat.messageAutoremoveTimeout canSetupAutoremoveTimeout = false - } else if let group = chatPeer as? TelegramGroup { + } else if case let .legacyGroup(group) = chatPeer { if !group.hasBannedPermission(.banChangeInfo) { canSetupAutoremoveTimeout = true } - } else if let user = chatPeer as? TelegramUser { + } else if case let .user(user) = chatPeer { if user.id != strongSelf.context.account.peerId { canSetupAutoremoveTimeout = true } - } else if let channel = chatPeer as? TelegramChannel { + } else if case let .channel(channel) = chatPeer { if channel.hasPermission(.changeInfo) { canSetupAutoremoveTimeout = true } @@ -460,7 +460,7 @@ extension PeerInfoScreenNode { } var hasDiscussion = false - if let channel = chatPeer as? TelegramChannel { + if case let .channel(channel) = chatPeer { switch channel.info { case let .broadcast(info): hasDiscussion = info.flags.contains(.hasDiscussionGroup) @@ -854,7 +854,7 @@ extension PeerInfoScreenNode { generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) }, action: { c, _ in if let c { - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: EnginePeer(user)) } }))) } @@ -1064,13 +1064,13 @@ extension PeerInfoScreenNode { }))) } - let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: channel, chatPeer: channel, cachedData: strongSelf.data?.cachedData) + let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: channel, chatPeer: EnginePeer(channel), cachedData: strongSelf.data?.cachedData) if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) }, action: { c, _ in if let c { - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: channel, chatPeer: channel) + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: channel, chatPeer: EnginePeer(channel)) } }))) } @@ -1231,13 +1231,13 @@ extension PeerInfoScreenNode { }))) } - let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: group, chatPeer: group, cachedData: strongSelf.data?.cachedData) + let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: group, chatPeer: EnginePeer(group), cachedData: strongSelf.data?.cachedData) if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) }, action: { c, _ in if let c { - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: group, chatPeer: group) + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: group, chatPeer: EnginePeer(group)) } }))) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift index a1380a9d1e..b2e43e5e9c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift @@ -128,7 +128,7 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat return context.engine.stickers.resolveInlineStickers(fileIds: fileIds) } )) - let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer._asPeer())) + let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer)) items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: mappedContext, enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, isAccount: true, action: { action in switch action { case .open: @@ -174,7 +174,7 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat if let settings = data.globalSettings { for bot in settings.bots { let iconSignal: Signal - if let peer = PeerReference(bot.peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] { + if let peer = PeerReference(bot.peer), let icon = bot.icons[.iOSSettingsStatic] { let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon) iconSignal = instantPageImageFile(account: context.account, userLocation: .other, fileReference: fileReference, fetched: true) |> map { generator -> UIImage? in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift index 805389e0ed..2ff46cd6a4 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift @@ -11,7 +11,7 @@ import SendInviteLinkScreen import UndoUI import PresentationDataUtils -public func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { +public func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: EnginePeer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { let members: Promise<[PeerId]> = Promise() if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { /*var membersDisposable: Disposable? @@ -35,7 +35,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } var canCreateInviteLink = false - if let group = groupPeer as? TelegramGroup { + if case let .legacyGroup(group) = groupPeer { switch group.role { case .creator: canCreateInviteLink = true @@ -44,7 +44,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa default: break } - } else if let channel = groupPeer as? TelegramChannel, (channel.addressName?.isEmpty ?? true) { + } else if case let .channel(channel) = groupPeer, (channel.addressName?.isEmpty ?? true) { if channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) { canCreateInviteLink = true } @@ -207,7 +207,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa if !failedPeers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { var viewControllers = navigationController.viewControllers if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { - let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: EnginePeer(groupPeer), link: exportedInvitation?.link), peers: failedPeers) + let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: groupPeer, link: exportedInvitation?.link), peers: failedPeers) viewControllers.remove(at: index) viewControllers.append(inviteScreen) navigationController.setViewControllers(viewControllers, animated: true) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift index f8b8971285..e6443db7b5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift @@ -242,7 +242,7 @@ final class PeerInfoStoryGridScreenComponent: Component { guard let self, let component = self.component, let peer else { return } - guard let peerReference = PeerReference(peer._asPeer()) else { + guard let peerReference = PeerReference(peer) else { return } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index e1ad235d1c..b5ada88e29 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -489,7 +489,7 @@ private final class DurationLayer: SimpleLayer { }) self.contents = image?.cgImage - if let smallProfileImage = author.smallProfileImage, let peerReference = PeerReference(author._asPeer()) { + if let smallProfileImage = author.smallProfileImage, let peerReference = PeerReference(author) { if let result = directMediaImageCache.getAvatarImage(peer: peerReference, resource: MediaResourceReference.avatar(peer: peerReference, resource: smallProfileImage.resource), immediateThumbnail: smallProfileImage.immediateThumbnailData, size: 24, includeBlurred: true, synchronous: synchronous == .full) { if let image = result.image { avatarLayer.contents = image.cgImage @@ -2766,7 +2766,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr guard let self else { return } - guard let peer, let peerReference = PeerReference(peer._asPeer()) else { + guard let peer, let peerReference = PeerReference(peer) else { return } @@ -3060,7 +3060,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr if let value = state.peerReference { peerReference = value } else if let peer = item.peer { - peerReference = PeerReference(peer._asPeer()) + peerReference = PeerReference(peer) } guard let peerReference else { continue diff --git a/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsController.swift b/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsController.swift index 743d9178cb..db24d5d6ca 100644 --- a/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsController.swift +++ b/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsController.swift @@ -45,7 +45,7 @@ func localizedOldChannelDate(peer: InactiveChannel, strings: PresentationStrings string = strings.OldChannels_InactiveYear(Int32(dif)) } - if let channel = peer.peer as? TelegramChannel, case .group = channel.info { + if case let .channel(channel) = peer.peer, case .group = channel.info { if let participantsCount = peer.participantsCount, participantsCount != 0 { string = strings.OldChannels_GroupFormat(participantsCount) + ", " + string } else { @@ -172,7 +172,7 @@ private enum OldChannelsEntry: ItemListNodeEntry { case let .peersHeader(title): return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section) case let .peer(_, peer, selected): - return ContactsPeerItem(presentationData: presentationData, style: .blocks, systemStyle: .glass, sectionId: self.section, sortOrder: .firstLast, displayOrder: .firstLast, context: arguments.context, peerMode: .peer, peer: .peer(peer: EnginePeer(peer.peer), chatPeer: EnginePeer(peer.peer)), status: .custom(string: NSAttributedString(string: localizedOldChannelDate(peer: peer, strings: presentationData.strings)), multiline: false, isActive: false, icon: nil), badge: nil, enabled: true, selection: ContactsPeerItemSelection.selectable(selected: selected), editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), options: [], actionIcon: .none, index: nil, header: nil, action: { _ in + return ContactsPeerItem(presentationData: presentationData, style: .blocks, systemStyle: .glass, sectionId: self.section, sortOrder: .firstLast, displayOrder: .firstLast, context: arguments.context, peerMode: .peer, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .custom(string: NSAttributedString(string: localizedOldChannelDate(peer: peer, strings: presentationData.strings)), multiline: false, isActive: false, icon: nil), badge: nil, enabled: true, selection: ContactsPeerItemSelection.selectable(selected: selected), editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), options: [], actionIcon: .none, index: nil, header: nil, action: { _ in arguments.togglePeer(peer.peer.id, true) }, setPeerIdWithRevealedOptions: nil, deletePeer: nil, itemHighlighting: nil, contextAction: nil) } @@ -386,7 +386,7 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa let peers = peers ?? [] let ensureStoredPeers = peers.map { $0.peer }.filter { state.selectedPeers.contains($0.id) } - let ensureStoredPeersSignal: Signal = context.engine.peers.ensurePeersAreLocallyAvailable(peers: ensureStoredPeers.map(EnginePeer.init)) + let ensureStoredPeersSignal: Signal = context.engine.peers.ensurePeersAreLocallyAvailable(peers: ensureStoredPeers) return ensureStoredPeersSignal |> then(context.engine.peers.removePeerChats(peerIds: Array(ensureStoredPeers.map(\.id)))) diff --git a/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift b/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift index 8259a5ab74..2b85b10410 100644 --- a/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift +++ b/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift @@ -154,7 +154,7 @@ private enum OldChannelsSearchEntry: Comparable, Identifiable { func item(context: AccountContext, presentationData: ItemListPresentationData, interaction: OldChannelsSearchInteraction) -> ListViewItem { switch self { case let .peer(_, peer, selected): - return ContactsPeerItem(presentationData: presentationData, style: .plain, sortOrder: .firstLast, displayOrder: .firstLast, context: context, peerMode: .peer, peer: .peer(peer: EnginePeer(peer.peer), chatPeer: EnginePeer(peer.peer)), status: .custom(string: NSAttributedString(string: localizedOldChannelDate(peer: peer, strings: presentationData.strings)), multiline: false, isActive: false, icon: nil), badge: nil, enabled: true, selection: ContactsPeerItemSelection.selectable(selected: selected), editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), options: [], actionIcon: .none, index: nil, header: nil, action: { _ in + return ContactsPeerItem(presentationData: presentationData, style: .plain, sortOrder: .firstLast, displayOrder: .firstLast, context: context, peerMode: .peer, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .custom(string: NSAttributedString(string: localizedOldChannelDate(peer: peer, strings: presentationData.strings)), multiline: false, isActive: false, icon: nil), badge: nil, enabled: true, selection: ContactsPeerItemSelection.selectable(selected: selected), editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), options: [], actionIcon: .none, index: nil, header: nil, action: { _ in interaction.togglePeer(peer.peer.id) }, setPeerIdWithRevealedOptions: nil, deletePeer: nil, itemHighlighting: nil, contextAction: nil) } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index 860ba6ad33..fb257f9244 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -356,7 +356,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.peerSelectionNode.requestOpenPeerFromSearch = { [weak self] peer, threadId in if let strongSelf = self { - strongSelf.openMessageFromSearchDisposable.set((_internal_storedMessageFromSearchPeer(postbox: strongSelf.context.account.postbox, peer: peer._asPeer()) + strongSelf.openMessageFromSearchDisposable.set((_internal_storedMessageFromSearchPeer(postbox: strongSelf.context.account.postbox, peer: peer) |> deliverOnMainQueue).start(completed: { [weak strongSelf] in if let strongSelf = strongSelf, let peerSelected = strongSelf.peerSelected { if case let .channel(peer) = peer, peer.isForumOrMonoForum, threadId == nil, strongSelf.selectForumThreads { diff --git a/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/GiftAvatarComponent.swift b/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/GiftAvatarComponent.swift index d0a78d1da8..a5ac5752eb 100644 --- a/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/GiftAvatarComponent.swift +++ b/submodules/TelegramUI/Components/Premium/PremiumStarComponent/Sources/GiftAvatarComponent.swift @@ -394,7 +394,7 @@ public final class GiftAvatarComponent: Component { self.mergedAvatarsNode = mergedAvatarsNode } - mergedAvatarsNode.update(context: component.context, peers: Array(component.peers.map { $0._asPeer() }.prefix(3)), synchronousLoad: false, imageSize: avatarSize.width, imageSpacing: 30.0, borderWidth: 2.0, avatarFontSize: 26.0) + mergedAvatarsNode.update(context: component.context, peers: Array(component.peers.prefix(3)), synchronousLoad: false, imageSize: avatarSize.width, imageSpacing: 30.0, borderWidth: 2.0, avatarFontSize: 26.0) let avatarsSize = CGSize(width: avatarSize.width + 30.0 * CGFloat(min(3, component.peers.count) - 1), height: avatarSize.height) mergedAvatarsNode.updateLayout(size: avatarsSize) mergedAvatarsNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - avatarsSize.width) / 2.0), y: 113.0 - avatarSize.height / 2.0), size: avatarsSize) diff --git a/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift b/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift index 94763a1b21..5f5f534e36 100644 --- a/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift +++ b/submodules/TelegramUI/Components/Settings/SettingsThemeWallpaperNode/Sources/SettingsThemeWallpaperNode.swift @@ -375,7 +375,7 @@ public final class SettingsThemeWallpaperNode: ASDisplayNode { animatedStickerNode.autoplay = true animatedStickerNode.visibility = true - self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource)).start()) + self.stickerFetchedDisposable.set(context.engine.resources.fetch(reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource), userLocation: .other, userContentType: .other).start()) // let thumbnailDimensions = PixelDimensions(width: 512, height: 512) // self.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: file.immediateThumbnailData, size: emojiFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: thumbnailDimensions.cgSize) diff --git a/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift index 060bc7539c..ed537cfedb 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift @@ -469,7 +469,7 @@ private final class ThemeCarouselThemeItemIconNode: ListViewItemNode { animatedStickerNode.autoplay = true animatedStickerNode.visibility = strongSelf.visibilityStatus - strongSelf.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: item.context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource)).start()) + strongSelf.stickerFetchedDisposable.set(item.context.engine.resources.fetch(reference: MediaResourceReference.media(media: .standalone(media: file), resource: file.resource), userLocation: .other, userContentType: .sticker).start()) let thumbnailDimensions = PixelDimensions(width: 512, height: 512) strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: file.immediateThumbnailData, size: emojiFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: thumbnailDimensions.cgSize) diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift index 2256c26de2..4b5d254dd2 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift @@ -794,7 +794,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: fileReference, representations: convertedRepresentations, alwaysShowThumbnailFirst: true, autoFetchFullSize: false) } - fetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: convertedRepresentations[convertedRepresentations.count - 1].reference) + fetchSignal = context.engine.resources.fetch(reference: convertedRepresentations[convertedRepresentations.count - 1].reference, userLocation: .other, userContentType: .other) let account = self.context.account statusSignal = self.context.sharedContext.accountManager.mediaBox.resourceStatus(file.file.resource) |> take(1) @@ -826,7 +826,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, alwaysShowThumbnailFirst: true, autoFetchFullSize: false) if let largestIndex = convertedRepresentations.firstIndex(where: { $0.representation == largestSize }) { - fetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: convertedRepresentations[largestIndex].reference) + fetchSignal = context.engine.resources.fetch(reference: convertedRepresentations[largestIndex].reference, userLocation: .other, userContentType: .other) } else { fetchSignal = .complete() } @@ -926,7 +926,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) signal = chatMessagePhoto(postbox: context.account.postbox, userLocation: .other, photoReference: .standalone(media: tmpImage)) - fetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: .media(media: .standalone(media: tmpImage), resource: imageResource)) + fetchSignal = context.engine.resources.fetch(reference: .media(media: .standalone(media: tmpImage), resource: imageResource), userLocation: .other, userContentType: .other) statusSignal = context.engine.resources.status(resource: EngineMediaResource(imageResource)) |> map { $0._asStatus() } } else { diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift index 9ee724f9b4..e7b05cc9f1 100644 --- a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift @@ -426,7 +426,7 @@ public class ShareRootControllerImpl { let accountData: Signal<(ShareControllerEnvironment, ShareControllerAccountContext, [ShareControllerSwitchableAccount]), NoError> = accountManager.accountRecords() |> take(1) |> mapToSignal { view -> Signal<(ShareControllerEnvironment, ShareControllerAccountContext, [ShareControllerSwitchableAccount]), NoError> in - var signals: [Signal<(AccountRecordId, AccountStateManager, Peer)?, NoError>] = [] + var signals: [Signal<(AccountRecordId, AccountStateManager, EnginePeer)?, NoError>] = [] for record in view.records { if record.attributes.contains(where: { attribute in if case .loggedOut = attribute { @@ -450,14 +450,14 @@ public class ShareRootControllerImpl { rootPath: rootPath, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: nil) ) - |> mapToSignal { result -> Signal<(AccountRecordId, AccountStateManager, Peer)?, NoError> in + |> mapToSignal { result -> Signal<(AccountRecordId, AccountStateManager, EnginePeer)?, NoError> in if let result { - return result.postbox.transaction { transaction -> (AccountRecordId, AccountStateManager, Peer)? in + return result.postbox.transaction { transaction -> (AccountRecordId, AccountStateManager, EnginePeer)? in guard let peer = transaction.getPeer(result.accountPeerId) else { return nil } - - return (record.id, result, peer) + + return (record.id, result, EnginePeer(peer)) } } else { return .single(nil) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index 307e2bc577..a034ae91d7 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -277,8 +277,8 @@ private final class StarsTransactionSheetContent: CombinedComponent { descriptionText = "" count = CurrencyAmount(amount: subscription.pricing.amount, currency: .stars) date = subscription.untilDate - if let creationDate = (subscription.peer._asPeer() as? TelegramChannel)?.creationDate, creationDate > 0 { - additionalDate = creationDate + if case let .channel(channel) = subscription.peer, channel.creationDate > 0 { + additionalDate = channel.creationDate } else { additionalDate = nil } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 91d7256e8f..2dfbc35fd6 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -1285,7 +1285,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer { var hasLeft = false var isKicked = false - if let channel = subscription.peer._asPeer() as? TelegramChannel { + if case let .channel(channel) = subscription.peer { switch channel.participationStatus { case .left: hasLeft = true diff --git a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift index 3e4d303edd..22f8fc3cfa 100644 --- a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift @@ -1320,7 +1320,7 @@ public final class PeerListItemComponent: Component { var mediaReference: AnyMediaReference? - if let peer = component.peer, let peerReference = PeerReference(peer._asPeer()) { + if let peer = component.peer, let peerReference = PeerReference(peer) { if let story = component.story { mediaReference = .story(peer: peerReference, id: story.id, media: story.media._asMedia()) } else if let message = component.message { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/LiveChatReactionStreamView.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/LiveChatReactionStreamView.swift index c80f66635f..1dd613c9f6 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/LiveChatReactionStreamView.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/LiveChatReactionStreamView.swift @@ -107,8 +107,8 @@ private func makePeerBadgeImage(engine: TelegramEngine, peer: EnginePeer, count: textSize.height = ceil(textSize.height) var avatarSourceImage: UIImage? - if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let peerReference = PeerReference(peer._asPeer()) { - let disposable = fetchedMediaResource(mediaBox: engine.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .avatar, reference: .avatar(peer: peerReference, resource: resource)).startStrict() + if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let peerReference = PeerReference(peer) { + let disposable = engine.resources.fetch(reference: .avatar(peer: peerReference, resource: resource), userLocation: .peer(peer.id), userContentType: .avatar).startStrict() let signal = engine.account.postbox.mediaBox.resourceData(resource) |> filter { $0.complete } |> map { value -> Data? in diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index 7c25b0979a..fbda84417d 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -207,9 +207,9 @@ public final class StoryContentContextImpl: StoryContentContext { if let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData { var isMuted = false if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { - isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings, topSearchPeers: []) + isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: notificationSettings, topSearchPeers: []) } else { - isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil, topSearchPeers: []) + isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: nil, topSearchPeers: []) } additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, @@ -1010,7 +1010,7 @@ public final class StoryContentContextImpl: StoryContentContext { for i in 0 ..< min(possibleItems.count, 3) { let peer = possibleItems[i].0 let item = possibleItems[i].1 - if let peerReference = PeerReference(peer._asPeer()), let mediaId = item.media.id { + if let peerReference = PeerReference(peer), let mediaId = item.media.id { var reactions: [MessageReaction.Reaction] = [] for mediaArea in item.mediaAreas { if case let .reaction(_, reaction, _) = mediaArea { @@ -1284,7 +1284,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext { return } - let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: []) + let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: []) let additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, @@ -1596,7 +1596,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { let stateValue: StoryContentContextState if let focusedIndex, let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging, boostsToUnrestrict, appliedBoosts, sendPaidMessageStars) = data?.data, let peer { - let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: []) + let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: []) let additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, areVoiceMessagesAvailable: areVoiceMessagesAvailable, @@ -1714,7 +1714,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { for i in 0 ..< min(possibleItems.count, 3) { let peer = possibleItems[i].0 let item = possibleItems[i].1 - if let peerReference = PeerReference(peer._asPeer()), let mediaId = item.storyItem.media.id { + if let peerReference = PeerReference(peer), let mediaId = item.storyItem.media.id { var reactions: [MessageReaction.Reaction] = [] for mediaArea in item.storyItem.mediaAreas { if case let .reaction(_, reaction, _) = mediaArea { @@ -1916,7 +1916,7 @@ public func preloadStoryMedia(context: AccountContext, info: StoryPreloadInfo) - return combineLatest(files.map { file -> Signal in return Signal { subscriber in - let loadSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)) + let loadSignal = context.engine.resources.fetch(reference: .standalone(resource: file.resource), userLocation: .other, userContentType: .sticker) |> ignoreValues |> `catch` { _ -> Signal in return .complete() @@ -1963,7 +1963,7 @@ public func preloadStoryMedia(context: AccountContext, info: StoryPreloadInfo) - return combineLatest(files.map { file -> Signal in return Signal { subscriber in - let loadSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)) + let loadSignal = context.engine.resources.fetch(reference: .standalone(resource: file.resource), userLocation: .other, userContentType: .sticker) |> ignoreValues |> `catch` { _ -> Signal in return .complete() @@ -2028,7 +2028,7 @@ public func waitUntilStoryMediaPreloaded(context: AccountContext, peerId: Engine guard let peerValue else { return .complete() } - guard let peer = PeerReference(peerValue._asPeer()) else { + guard let peer = PeerReference(peerValue) else { return .complete() } @@ -2152,7 +2152,7 @@ public func waitUntilStoryMediaPreloaded(context: AccountContext, peerId: Engine return combineLatest(files.map { file -> Signal in return Signal { subscriber in - let loadSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)) + let loadSignal = context.engine.resources.fetch(reference: .standalone(resource: file.resource), userLocation: .other, userContentType: .sticker) |> ignoreValues |> `catch` { _ -> Signal in return .complete() @@ -2201,7 +2201,7 @@ public func waitUntilStoryMediaPreloaded(context: AccountContext, peerId: Engine return combineLatest(files.map { file -> Signal in return Signal { subscriber in - let loadSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)) + let loadSignal = context.engine.resources.fetch(reference: .standalone(resource: file.resource), userLocation: .other, userContentType: .sticker) |> ignoreValues |> `catch` { _ -> Signal in return .complete() @@ -2487,9 +2487,9 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { if let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData { var isMuted = false if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { - isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings, topSearchPeers: []) + isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: notificationSettings, topSearchPeers: []) } else { - isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil, topSearchPeers: []) + isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer, peerSettings: nil, topSearchPeers: []) } additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, @@ -2974,7 +2974,7 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { for i in 0 ..< min(possibleItems.count, 3) { let peer = possibleItems[i].0 let item = possibleItems[i].1 - if let peerReference = PeerReference(peer._asPeer()), let mediaId = item.media.id { + if let peerReference = PeerReference(peer), let mediaId = item.media.id { var reactions: [MessageReaction.Reaction] = [] for mediaArea in item.mediaAreas { if case let .reaction(_, reaction, _) = mediaArea { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 75fe51d2a7..8bbee1abe6 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -335,7 +335,7 @@ final class StoryItemContentComponent: Component { return } - if case let .file(file) = currentMessageMedia, let peerReference = PeerReference(component.peer._asPeer()) { + if case let .file(file) = currentMessageMedia, let peerReference = PeerReference(component.peer) { if self.videoNode == nil { let videoNode = UniversalVideoNode( context: component.context, @@ -843,7 +843,7 @@ final class StoryItemContentComponent: Component { let startTime = CFAbsoluteTimeGetCurrent() - let peerReference = PeerReference(component.peer._asPeer()) + let peerReference = PeerReference(component.peer) let selectedMedia: EngineMedia var messageMedia: EngineMedia? diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift index 516c8d872b..adf39cf105 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift @@ -102,7 +102,7 @@ final class StoryItemImageView: UIView { } } - if let peerReference = PeerReference(peer._asPeer()) { + if let peerReference = PeerReference(peer) { self.fetchDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(peer.id), userContentType: .story, reference: .media(media: .story(peer: peerReference, id: storyId, media: media._asMedia()), resource: representation.resource), ranges: nil).start() } self.disposable = (context.engine.resources.data(resource: EngineMediaResource(representation.resource)) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift index 72f8685b71..61cdc25fd4 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift @@ -425,7 +425,7 @@ final class StoryItemOverlaysView: UIView { self.directStickerView = directStickerView self.customEmojiLoadDisposable?.dispose() - self.customEmojiLoadDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)).start() + self.customEmojiLoadDisposable = context.engine.resources.fetch(reference: .standalone(resource: file.resource), userLocation: .other, userContentType: .sticker).start() } var color: UIColor? if file.isCustomTemplateEmoji { @@ -474,7 +474,7 @@ final class StoryItemOverlaysView: UIView { customEmojiView.updateTextColor(flags.contains(.isDark) ? .white : .black) self.customEmojiLoadDisposable?.dispose() - self.customEmojiLoadDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)).start() + self.customEmojiLoadDisposable = context.engine.resources.fetch(reference: .standalone(resource: file.resource), userLocation: .other, userContentType: .sticker).start() customEmojiView.isUserInteractionEnabled = false self.customEmojiView = customEmojiView @@ -594,7 +594,7 @@ final class StoryItemOverlaysView: UIView { self.file = file self.customEmojiLoadDisposable?.dispose() - self.customEmojiLoadDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)).start() + self.customEmojiLoadDisposable = context.engine.resources.fetch(reference: .standalone(resource: file.resource), userLocation: .other, userContentType: .sticker).start() let _ = self.directStickerView.update( transition: .immediate, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 825e8e4170..242dc1e3d3 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -5967,7 +5967,7 @@ public final class StoryItemSetContainerComponent: Component { } private func requestSave() { - guard let component = self.component, let peerReference = PeerReference(component.slice.effectivePeer._asPeer()) else { + guard let component = self.component, let peerReference = PeerReference(component.slice.effectivePeer) else { return } @@ -6243,7 +6243,7 @@ public final class StoryItemSetContainerComponent: Component { } } - if !emojiFileIds.isEmpty || hasLinkedStickers, let peerReference = PeerReference(component.slice.effectivePeer._asPeer()) { + if !emojiFileIds.isEmpty || hasLinkedStickers, let peerReference = PeerReference(component.slice.effectivePeer) { let context = component.context tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil) @@ -6697,7 +6697,7 @@ public final class StoryItemSetContainerComponent: Component { } } - if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { + if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer.usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -6953,7 +6953,7 @@ public final class StoryItemSetContainerComponent: Component { }))) } - if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { + if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer.usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -7221,7 +7221,7 @@ public final class StoryItemSetContainerComponent: Component { items.append(.separator) } - let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: component.slice.effectivePeer._asPeer(), peerSettings: settings._asNotificationSettings(), topSearchPeers: topSearchPeers) + let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: component.slice.effectivePeer, peerSettings: settings._asNotificationSettings(), topSearchPeers: topSearchPeers) if !component.slice.effectivePeer.isService && isContact { items.append(.action(ContextMenuActionItem(text: isMuted ? component.strings.StoryFeed_ContextNotifyOn : component.strings.StoryFeed_ContextNotifyOff, icon: { theme in @@ -7271,7 +7271,7 @@ public final class StoryItemSetContainerComponent: Component { }))) } - if !component.slice.effectivePeer.isService && component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) { + if !component.slice.effectivePeer.isService && component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer.usernames.isEmpty) { items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -7548,7 +7548,7 @@ public final class StoryItemSetContainerComponent: Component { } private func performMusicAction(file: TelegramMediaFile, sourceView: UIView, gesture: ContextGesture?) { - guard let component = self.component, let controller = component.controller(), let peer = PeerReference(component.slice.peer._asPeer()) else { + guard let component = self.component, let controller = component.controller(), let peer = PeerReference(component.slice.peer) else { return } diff --git a/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift b/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift index 0a7d00c8e3..446e8d1910 100644 --- a/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent/Sources/StorySetIndicatorComponent.swift @@ -182,7 +182,7 @@ public final class StorySetIndicatorComponent: Component { init(context: AccountContext, item: StorySetIndicatorComponent.Item, displayAvatars: Bool, updated: @escaping () -> Void) { self.updated = updated - let peerReference = PeerReference(item.peer._asPeer()) + let peerReference = PeerReference(item.peer) var messageMedia: EngineMedia? switch item.storyItem.media { diff --git a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift index 2d856b6e6f..892d86ba40 100644 --- a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift +++ b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift @@ -1278,7 +1278,7 @@ private final class TextProcessingSheetComponent: Component { currentPrice: nil, hasTimers: false, sendPaidMessageStars: nil, - isMonoforum: peer._asPeer().isMonoForum + isMonoforum: peer.isMonoForum )), hasEntityKeyboard: false, gesture: nil, diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 3490d978ea..bf272f5846 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -4003,7 +4003,7 @@ extension ChatControllerImpl { if !(peer is TelegramGroup || peer is TelegramChannel) { return } - presentAddMembersImpl(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, parentController: strongSelf, groupPeer: peer, selectAddMemberDisposable: strongSelf.selectAddMemberDisposable, addMemberDisposable: strongSelf.addMemberDisposable) + presentAddMembersImpl(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, parentController: strongSelf, groupPeer: EnginePeer(peer), selectAddMemberDisposable: strongSelf.selectAddMemberDisposable, addMemberDisposable: strongSelf.addMemberDisposable) }, presentGigagroupHelp: { [weak self] in if let strongSelf = self { strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Conversation_GigagroupDescription, timeout: nil, customUndoText: nil), elevatedLayout: false, action: { _ in return true }), in: .current) @@ -4967,7 +4967,7 @@ extension ChatControllerImpl { } let engine = self.context.engine - let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:]) + let previousPeerCache = Atomic<[PeerId: EnginePeer]>(value: [:]) let activitySpace: PeerActivitySpace? switch self.chatLocation { @@ -4982,9 +4982,9 @@ extension ChatControllerImpl { if let activitySpace = activitySpace, let peerId = peerId { self.peerInputActivitiesDisposable?.dispose() self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: activitySpace) - |> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in + |> mapToSignal { activities -> Signal<[(EnginePeer, PeerInputActivity)], NoError> in var foundAllPeers = true - var cachedResult: [(Peer, PeerInputActivity)] = [] + var cachedResult: [(EnginePeer, PeerInputActivity)] = [] previousPeerCache.with { dict -> Void in for (peerId, activity) in activities { if let peer = dict[peerId] { @@ -5001,13 +5001,13 @@ extension ChatControllerImpl { return engine.data.get(EngineDataMap( activities.map { TelegramEngine.EngineData.Item.Peer.Peer(id: $0.0) } )) - |> map { peerMap -> [(Peer, PeerInputActivity)] in - var result: [(Peer, PeerInputActivity)] = [] - var peerCache: [PeerId: Peer] = [:] + |> map { peerMap -> [(EnginePeer, PeerInputActivity)] in + var result: [(EnginePeer, PeerInputActivity)] = [] + var peerCache: [PeerId: EnginePeer] = [:] for (peerId, activity) in activities { if let maybePeer = peerMap[peerId], let peer = maybePeer { - result.append((peer._asPeer(), activity)) - peerCache[peerId] = peer._asPeer() + result.append((peer, activity)) + peerCache[peerId] = peer } } let _ = previousPeerCache.swap(peerCache) @@ -5030,7 +5030,7 @@ extension ChatControllerImpl { peerId: peerId, items: displayActivities.map { item -> ChatTitleComponent.Activities.Item in return ChatTitleComponent.Activities.Item( - peer: EnginePeer(item.0), + peer: item.0, activity: item.1 ) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index d5f9e2a6ee..c3fbab0819 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -386,7 +386,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var chatUnreadMentionCountDisposable: Disposable? var peerInputActivitiesDisposable: Disposable? - var peerInputActivitiesPromise = Promise<[(Peer, PeerInputActivity)]>() + var peerInputActivitiesPromise = Promise<[(EnginePeer, PeerInputActivity)]>() var interactiveEmojiSyncDisposable = MetaDisposable() var recentlyUsedInlineBotsValue: [Peer] = [] diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 6fc1835df1..324fd0e7e6 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -735,7 +735,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { if fileReference.media.isAnimatedSticker { let dimensions = fileReference.media.dimensions ?? PixelDimensions(width: 512, height: 512) updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), file: fileReference.media, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))) - updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(fileReference.media.resource)) + updatedFetchMediaSignal = context.engine.resources.fetch(reference: fileReference.resourceReference(fileReference.media.resource), userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: fileReference.media)) } else if fileReference.media.isVideo || fileReference.media.isAnimated { updateImageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), fileReference: fileReference, blurred: hasSpoiler) } else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) { diff --git a/submodules/TelegramUI/Sources/MentionChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/MentionChatInputContextPanelNode.swift index c0d7a4c6ca..41ee283032 100644 --- a/submodules/TelegramUI/Sources/MentionChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/MentionChatInputContextPanelNode.swift @@ -36,7 +36,7 @@ private struct MentionChatInputContextPanelEntry: Comparable, Identifiable { } func item(context: AccountContext, presentationData: PresentationData, inverted: Bool, setPeerIdRevealed: @escaping (EnginePeer.Id?) -> Void, peerSelected: @escaping (EnginePeer) -> Void, removeRequested: @escaping (EnginePeer.Id) -> Void) -> ListViewItem { - return MentionChatInputPanelItem(context: context, presentationData: ItemListPresentationData(presentationData), inverted: inverted, peer: self.peer._asPeer(), revealed: self.revealed, setPeerIdRevealed: setPeerIdRevealed, peerSelected: peerSelected, removeRequested: removeRequested) + return MentionChatInputPanelItem(context: context, presentationData: ItemListPresentationData(presentationData), inverted: inverted, peer: self.peer, revealed: self.revealed, setPeerIdRevealed: setPeerIdRevealed, peerSelected: peerSelected, removeRequested: removeRequested) } } diff --git a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift index 928dba8aa7..4ab767c87d 100644 --- a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift @@ -16,14 +16,14 @@ final class MentionChatInputPanelItem: ListViewItem { fileprivate let presentationData: ItemListPresentationData fileprivate let revealed: Bool fileprivate let inverted: Bool - fileprivate let peer: Peer + fileprivate let peer: EnginePeer private let peerSelected: (EnginePeer) -> Void fileprivate let setPeerIdRevealed: (EnginePeer.Id?) -> Void fileprivate let removeRequested: (EnginePeer.Id) -> Void - + let selectable: Bool = true - - public init(context: AccountContext, presentationData: ItemListPresentationData, inverted: Bool, peer: Peer, revealed: Bool, setPeerIdRevealed: @escaping (PeerId?) -> Void, peerSelected: @escaping (EnginePeer) -> Void, removeRequested: @escaping (PeerId) -> Void) { + + public init(context: AccountContext, presentationData: ItemListPresentationData, inverted: Bool, peer: EnginePeer, revealed: Bool, setPeerIdRevealed: @escaping (PeerId?) -> Void, peerSelected: @escaping (EnginePeer) -> Void, removeRequested: @escaping (PeerId) -> Void) { self.context = context self.presentationData = presentationData self.inverted = inverted @@ -85,7 +85,7 @@ final class MentionChatInputPanelItem: ListViewItem { if self.revealed { self.setPeerIdRevealed(nil) } else { - self.peerSelected(EnginePeer(self.peer)) + self.peerSelected(self.peer) } } } @@ -174,7 +174,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { } - let title = EnginePeer(item.peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + let title = item.peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) var username: String? let string = NSMutableAttributedString() string.append(NSAttributedString(string: title, font: primaryFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)) @@ -204,7 +204,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor) let _ = textApply() @@ -221,7 +221,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { strongSelf.activateAreaNode.accessibilityValue = username strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: nodeLayout.size) - if let peer = item.peer as? TelegramUser, let _ = peer.botInfo { + if case let .user(peer) = item.peer, let _ = peer.botInfo { strongSelf.setRevealOptions([ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)]) strongSelf.setRevealOptionsOpened(item.revealed, animated: animation.isAnimated) } else { diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 432d69fc68..5c618084fc 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -745,7 +745,7 @@ func openResolvedUrlImpl( subscriber.putNext((nil, settings, themeInfo)) subscriber.putCompletion() } else if let resource = themeInfo.file?.resource { - disposables.add(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: .standalone(resource: resource)).start()) + disposables.add(context.engine.resources.fetch(reference: .standalone(resource: resource), userLocation: .other, userContentType: .other).start()) let maybeFetched = context.sharedContext.accountManager.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false) |> mapToSignal { maybeData -> Signal in diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 281156da9c..bb46df404b 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -600,7 +600,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: savedMusicContext.peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer = peer.flatMap({ PeerReference($0._asPeer()) }) else { + guard let self, let peer = peer.flatMap({ PeerReference($0) }) else { return } diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift index fb1bb8ed30..5ba432e116 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControlsNode.swift @@ -3,7 +3,6 @@ import UIKit import AsyncDisplayKit import Display import ComponentFlow -import Postbox import TelegramCore import SwiftSignalKit import TelegramPresentationData @@ -453,7 +452,7 @@ final class OverlayAudioPlayerControlsNode: ASDisplayNode { case let .telegramFile(fileReference, _, _): strongSelf.currentFileReference = fileReference if let size = fileReference.media.size { - strongSelf.scrubberNode.bufferingStatus = strongSelf.account.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource) + strongSelf.scrubberNode.bufferingStatus = strongSelf.engine.resources.resourceRangesStatus(resource: EngineMediaResource(fileReference.media.resource)) |> map { ranges -> (RangeSet, Int64) in return (ranges, size) } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index eeadb9a725..805fade423 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -808,7 +808,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { guard let peer = peer else { return nil } - return AccountWithInfo(account: context.account, peer: peer._asPeer()) + return AccountWithInfo(account: context.account, peer: peer) } |> distinctUntilChanged }) @@ -1935,7 +1935,6 @@ public final class SharedAccountContextImpl: SharedAccountContext { } public func makePeerInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peer: EnginePeer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool, requestsContext: PeerInvitationImportersContext?) -> ViewController? { - let peer = peer._asPeer() let controller = peerInfoControllerImpl(context: context, updatedPresentationData: updatedPresentationData, peer: peer, mode: mode, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: fromChat) controller?.navigationPresentation = .modalInLargeLayout return controller @@ -2300,7 +2299,6 @@ public final class SharedAccountContextImpl: SharedAccountContext { } public func makeChatRecentActionsController(context: AccountContext, peer: EnginePeer, adminPeerId: PeerId?, starsState: StarsRevenueStats?) -> ViewController { - let peer = peer._asPeer() return ChatRecentActionsController(context: context, peer: peer, adminPeerId: adminPeerId, starsState: starsState) } @@ -2348,7 +2346,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return PeerSelectionControllerImpl(params) } - public func openAddPeerMembers(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { + public func openAddPeerMembers(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: EnginePeer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { return presentAddMembersImpl(context: context, updatedPresentationData: updatedPresentationData, parentController: parentController, groupPeer: groupPeer, selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable) } @@ -2729,7 +2727,6 @@ public final class SharedAccountContextImpl: SharedAccountContext { } public func makeChatQrCodeScreen(context: AccountContext, peer: EnginePeer, threadId: Int64?, temporary: Bool) -> ViewController { - let peer = peer._asPeer() return ChatQrCodeScreenImpl(context: context, subject: .peer(peer: peer, threadId: threadId, temporary: temporary)) } @@ -4434,7 +4431,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { } } -private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? { +private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal)?, peer: EnginePeer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? { var switchToMediaTarget: PeerInfoSwitchToMediaTarget? switch mode { case let .media(kind, index): @@ -4450,9 +4447,9 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation break } - if let _ = peer as? TelegramGroup { + if case .legacyGroup = peer { return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, reactionSourceMessageId: nil, callMessages: [], switchToMediaTarget: switchToMediaTarget) - } else if let _ = peer as? TelegramChannel { + } else if case .channel = peer { var sourceMessageId: MessageId? var forumTopicThread: ChatReplyThreadMessage? var switchToRecommendedChannels = false @@ -4478,7 +4475,7 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation break } return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, reactionSourceMessageId: nil, sourceMessageId: sourceMessageId, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder, switchToMediaTarget: switchToMediaTarget) - } else if peer is TelegramUser { + } else if case .user = peer { var reactionSourceMessageId: MessageId? var sourceMessageId: MessageId? var callMessages: [Message] = [] @@ -4531,7 +4528,7 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation break } return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, reactionSourceMessageId: reactionSourceMessageId, sourceMessageId: sourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder, switchToMediaTarget: switchToMediaTarget) - } else if peer is TelegramSecretChat { + } else if case .secretChat = peer { return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, reactionSourceMessageId: nil, callMessages: [], switchToMediaTarget: switchToMediaTarget) } return nil diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 8c7432495d..4359fbe72b 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -597,7 +597,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: resource._asResource(), animated: true) } if let resourceReference = resourceReference { - updatedFetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: resourceReference) + updatedFetchSignal = context.engine.resources.fetch(reference: resourceReference, userLocation: .other, userContentType: .other) |> mapError { _ -> EngineMediaResource.Fetch.Error in return .generic } @@ -914,7 +914,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: resource._asResource(), animated: true) } if let resourceReference = resourceReference { - updatedFetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: resourceReference) + updatedFetchSignal = context.engine.resources.fetch(reference: resourceReference, userLocation: .other, userContentType: .other) |> mapError { _ -> EngineMediaResource.Fetch.Error in return .generic } diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index dc19610a01..2da05a1503 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -344,7 +344,7 @@ public final class WebAppController: ViewController, AttachmentContainable { return .single(nil) } |> mapToSignal { bot -> Signal<(FileMediaReference, Bool)?, NoError> in - if let bot = bot, let peerReference = PeerReference(bot.peer._asPeer()) { + if let bot = bot, let peerReference = PeerReference(bot.peer) { var imageFile: TelegramMediaFile? var isPlaceholder = false if let file = bot.icons[.placeholder] {