From 85a0b3b4d0552ddbec3c8e86916152965136e050 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 20 May 2022 06:53:13 +0300 Subject: [PATCH] Refactoring --- .../Sources/ChatListController.swift | 3 +- .../Sources/CalendarMessageScreen.swift | 18 +- .../Sources/ChatListController.swift | 154 +++++++++--------- .../ChatListFilterPresetController.swift | 44 +++-- .../Sources/ChatListSearchContainerNode.swift | 94 ++++------- .../Sources/ChatListSearchListPaneNode.swift | 9 +- .../Sources/HashtagSearchController.swift | 2 +- .../Sources/MutableLocalNoticeEntryView.swift | 36 ++++ submodules/Postbox/Sources/Views.swift | 11 ++ submodules/TelegramApi/Sources/Api0.swift | 4 +- submodules/TelegramApi/Sources/Api11.swift | 24 +++ submodules/TelegramApi/Sources/Api25.swift | 22 ++- submodules/TelegramApi/Sources/Api27.swift | 18 ++ submodules/TelegramApi/Sources/Api4.swift | 12 ++ .../ApiUtils/StoreMessage_Telegram.swift | 78 ++++----- .../TextEntitiesMessageAttribute.swift | 2 + .../Network/FetchedMediaResource.swift | 2 +- .../ManagedSecretChatOutgoingOperations.swift | 4 + ...yncCore_TextEntitiesMessageAttribute.swift | 9 + .../TelegramEngine/Data/Configuration.swift | 22 +++ .../TelegramEngine/Data/Messages.swift | 6 +- .../Sources/TelegramEngine/Data/Notices.swift | 27 +++ .../TelegramEngine/Data/PeerSummary.swift | 2 +- .../Messages/TelegramEngineMessages.swift | 10 +- .../TelegramEngine/Messages/Translate.swift | 15 +- .../Peers/ChatListFiltering.swift | 4 + .../Peers/TelegramEnginePeers.swift | 4 + .../Utils}/StoredMessageFromSearchPeer.swift | 23 +-- .../Sources/AudioWaveformComponent.swift | 124 +++++++++++++- .../ChatMessageInteractiveFileNode.swift | 13 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 17 +- 31 files changed, 548 insertions(+), 265 deletions(-) create mode 100644 submodules/Postbox/Sources/MutableLocalNoticeEntryView.swift create mode 100644 submodules/TelegramCore/Sources/TelegramEngine/Data/Notices.swift rename submodules/{AccountContext/Sources => TelegramCore/Sources/Utils}/StoredMessageFromSearchPeer.swift (52%) diff --git a/submodules/AccountContext/Sources/ChatListController.swift b/submodules/AccountContext/Sources/ChatListController.swift index 41778e46c4..ea98a4b5e6 100644 --- a/submodules/AccountContext/Sources/ChatListController.swift +++ b/submodules/AccountContext/Sources/ChatListController.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Postbox import Display +import TelegramCore public protocol ChatListController: ViewController { var context: AccountContext { get } @@ -11,5 +12,5 @@ public protocol ChatListController: ViewController { func activateSearch(filter: ChatListSearchFilter, query: String?) func deactivateSearch(animated: Bool) func activateCompose() - func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) + func maybeAskForPeerChatRemoval(peer: EngineRenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) } diff --git a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift index 42e2fde306..a4f1b421d6 100644 --- a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift +++ b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift @@ -1428,7 +1428,7 @@ public final class CalendarMessageScreen: ViewController { struct ClearInfo { var canClearForMyself: ClearType? var canClearForEveryone: ClearType? - var mainPeer: Peer + var mainPeer: EnginePeer } let peerId = self.peerId @@ -1436,8 +1436,10 @@ public final class CalendarMessageScreen: ViewController { } else { return } - let _ = (self.context.account.postbox.transaction { transaction -> ClearInfo? in - guard let chatPeer = transaction.getPeer(peerId) else { + + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> map { chatPeer -> ClearInfo? in + guard let chatPeer = chatPeer else { return nil } @@ -1447,10 +1449,10 @@ public final class CalendarMessageScreen: ViewController { if peerId == self.context.account.peerId { canClearForMyself = .savedMessages canClearForEveryone = nil - } else if chatPeer is TelegramSecretChat { + } else if case .secretChat = chatPeer { canClearForMyself = .secretChat canClearForEveryone = nil - } else if let group = chatPeer as? TelegramGroup { + } else if case let .legacyGroup(group) = chatPeer { switch group.role { case .creator: canClearForMyself = .group @@ -1459,7 +1461,7 @@ public final class CalendarMessageScreen: ViewController { canClearForMyself = .group canClearForEveryone = nil } - } else if let channel = chatPeer as? TelegramChannel { + } else if case let .channel(channel) = chatPeer { if channel.hasPermission(.deleteAllMessages) { if case .group = channel.info { canClearForEveryone = .group @@ -1473,7 +1475,7 @@ public final class CalendarMessageScreen: ViewController { } else { 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 @@ -1511,7 +1513,7 @@ public final class CalendarMessageScreen: ViewController { let confirmationText: String switch canClearForEveryone { case .user: - text = strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(info.mainPeer).compactDisplayTitle).string + text = strongSelf.presentationData.strings.ChatList_DeleteForEveryone(info.mainPeer.compactDisplayTitle).string confirmationText = strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText default: text = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index d91976ebc4..2fcd7b798f 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -973,7 +973,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.chatListDisplayNode.requestOpenMessageFromSearch = { [weak self] peer, messageId, deactivateOnAction in if let strongSelf = self { - strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer()) + strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer) |> deliverOnMainQueue).start(next: { [weak strongSelf] actualPeerId in if let strongSelf = strongSelf { if let navigationController = strongSelf.navigationController as? NavigationController { @@ -995,13 +995,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.chatListDisplayNode.requestOpenPeerFromSearch = { [weak self] peer, dismissSearch in if let strongSelf = self { - let storedPeer = strongSelf.context.account.postbox.transaction { transaction -> Void in - if transaction.getPeer(peer.id) == nil { - updatePeers(transaction: transaction, peers: [peer._asPeer()], update: { previousPeer, updatedPeer in - return updatedPeer - }) - } - } + let storedPeer = strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer) |> map { _ -> Void in return Void() } strongSelf.openMessageFromSearchDisposable.set((storedPeer |> deliverOnMainQueue).start(completed: { [weak strongSelf] in if let strongSelf = strongSelf { if dismissSearch { @@ -1528,27 +1522,27 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.didSuggestLocalization = true let context = self.context - let signal = combineLatest(self.context.sharedContext.accountManager.transaction { transaction -> String in - let languageCode: String - if let current = transaction.getSharedData(SharedDataKeys.localizationSettings)?.get(LocalizationSettings.self) { - let code = current.primaryComponent.languageCode - let rawSuffix = "-raw" - if code.hasSuffix(rawSuffix) { - languageCode = String(code.dropLast(rawSuffix.count)) + + let suggestedLocalization = self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.SuggestedLocalization()) + + let signal = combineLatest( + self.context.sharedContext.accountManager.transaction { transaction -> String in + let languageCode: String + if let current = transaction.getSharedData(SharedDataKeys.localizationSettings)?.get(LocalizationSettings.self) { + let code = current.primaryComponent.languageCode + let rawSuffix = "-raw" + if code.hasSuffix(rawSuffix) { + languageCode = String(code.dropLast(rawSuffix.count)) + } else { + languageCode = code + } } else { - languageCode = code + languageCode = "en" } - } else { - languageCode = "en" - } - return languageCode - }, self.context.account.postbox.transaction { transaction -> SuggestedLocalizationEntry? in - var suggestedLocalization: SuggestedLocalizationEntry? - if let localization = transaction.getPreferencesEntry(key: PreferencesKeys.suggestedLocalization)?.get(SuggestedLocalizationEntry.self) { - suggestedLocalization = localization - } - return suggestedLocalization - }) + return languageCode + }, + suggestedLocalization + ) |> mapToSignal({ value -> Signal<(String, SuggestedLocalizationInfo)?, NoError> in guard let suggestedLocalization = value.1, !suggestedLocalization.isSeen && suggestedLocalization.languageCode != "en" && suggestedLocalization.languageCode != value.0 else { return .single(nil) @@ -1604,14 +1598,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController })) Queue.mainQueue().after(1.0, { - let _ = (self.context.account.postbox.transaction { transaction -> Int32? in - if let value = transaction.getNoticeEntry(key: ApplicationSpecificNotice.forcedPasswordSetupKey())?.get(ApplicationSpecificCounterNotice.self) { - return value.value - } else { - return nil + let _ = ( + self.context.engine.data.get(TelegramEngine.EngineData.Item.Notices.Notice(key: ApplicationSpecificNotice.forcedPasswordSetupKey())) + |> map { entry -> Int32? in + return entry?.get(ApplicationSpecificCounterNotice.self)?.value } - } - |> deliverOnMainQueue).start(next: { [weak self] value in + |> deliverOnMainQueue + ).start(next: { [weak self] value in guard let strongSelf = self else { return } @@ -2534,20 +2527,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } func deletePeerChat(peerId: PeerId, joined: Bool) { - let _ = (self.context.account.postbox.transaction { transaction -> RenderedPeer? in - guard let peer = transaction.getPeer(peerId) else { - return nil - } - if let associatedPeerId = peer.associatedPeerId { - if let associatedPeer = transaction.getPeer(associatedPeerId) { - return RenderedPeer(peerId: peerId, peers: SimpleDictionary([peer.id: peer, associatedPeer.id: associatedPeer])) - } else { - return nil - } - } else { - return RenderedPeer(peer: peer) - } - } + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.RenderedPeer(id: peerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let strongSelf = self, let peer = peer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { return @@ -2564,7 +2544,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController canRemoveGlobally = true } - if let user = chatPeer as? TelegramUser, user.botInfo == nil, canRemoveGlobally { + if case let .user(user) = chatPeer, user.botInfo == nil, canRemoveGlobally { strongSelf.maybeAskForPeerChatRemoval(peer: peer, joined: joined, completion: { _ in }, removed: {}) } else { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) @@ -2574,7 +2554,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController var canRemoveGlobally = false var deleteTitle = strongSelf.presentationData.strings.Common_Delete - if let channel = chatPeer as? TelegramChannel { + if case let .channel(channel) = chatPeer { if case .broadcast = channel.info { canClear = false deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel @@ -2590,30 +2570,38 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let addressName = channel.addressName, !addressName.isEmpty { canClear = false } - } else if let group = chatPeer as? TelegramGroup { + } else if case let .legacyGroup(group) = chatPeer { if case .creator = group.role { canRemoveGlobally = true } - } else if let user = chatPeer as? TelegramUser, user.botInfo != nil { + } else if case let .user(user) = chatPeer, user.botInfo != nil { canStop = !user.flags.contains(.isSupport) canClear = user.botInfo == nil deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat - } else if let _ = chatPeer as? TelegramSecretChat { + } else if case .secretChat = chatPeer { canClear = true deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat } let limitsConfiguration = strongSelf.context.currentLimitsConfiguration.with { $0 } - if chatPeer is TelegramUser && chatPeer.id != strongSelf.context.account.peerId { + if case .user = chatPeer, chatPeer.id != strongSelf.context.account.peerId { if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever { canRemoveGlobally = true } - } else if chatPeer is TelegramSecretChat { + } else if case .secretChat = chatPeer { canRemoveGlobally = true } - if canRemoveGlobally, (mainPeer is TelegramGroup || mainPeer is TelegramChannel) { - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + var isGroupOrChannel = false + switch mainPeer { + case .legacyGroup, .channel: + isGroupOrChannel = true + default: + break + } + + if canRemoveGlobally && isGroupOrChannel { + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() @@ -2622,7 +2610,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController })) let deleteForAllText: String - if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info { + if case let .channel(channel) = mainPeer, case .broadcast = channel.info { deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribers } else { deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllMembers @@ -2635,7 +2623,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } let deleteForAllConfirmation: String - if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info { + if case let .channel(channel) = mainPeer, case .broadcast = channel.info { deleteForAllConfirmation = strongSelf.presentationData.strings.ChannelInfo_DeleteChannelConfirmation } else { deleteForAllConfirmation = strongSelf.presentationData.strings.ChannelInfo_DeleteGroupConfirmation @@ -2651,7 +2639,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController ], parseMarkdown: true), in: .window(.root)) })) } else { - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .delete, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) if canClear { let beginClear: (InteractiveHistoryClearingType) -> Void = { type in @@ -2705,14 +2693,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - if chatPeer is TelegramSecretChat { + if case .secretChat = chatPeer { beginClear(.forEveryone) } else { if canRemoveGlobally { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .clearHistory(canClearCache: false), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory(canClearCache: false), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) if joined || mainPeer.isDeleted { items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in @@ -2724,7 +2712,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController beginClear(.forLocalPeer) actionSheet?.dismissAnimated() })) - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in beginClear(.forEveryone) actionSheet?.dismissAnimated() })) @@ -2752,8 +2740,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController })) } - if chatPeer is TelegramSecretChat { - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in + if case .secretChat = chatPeer { + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() guard let strongSelf = self else { return @@ -2768,11 +2756,19 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - if canRemoveGlobally, (mainPeer is TelegramGroup || mainPeer is TelegramChannel) { + var isGroupOrChannel = false + switch mainPeer { + case .legacyGroup, .channel: + isGroupOrChannel = true + default: + break + } + + if canRemoveGlobally && isGroupOrChannel { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() @@ -2781,7 +2777,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController })) let deleteForAllText: String - if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info { + if case let .channel(channel) = mainPeer, case .broadcast = channel.info { deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribers } else { deleteForAllText = strongSelf.presentationData.strings.ChatList_DeleteForAllMembers @@ -2794,7 +2790,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } let deleteForAllConfirmation: String - if let channel = mainPeer as? TelegramChannel, case .broadcast = channel.info { + if case let .channel(channel) = mainPeer, case .broadcast = channel.info { deleteForAllConfirmation = strongSelf.presentationData.strings.ChatList_DeleteForAllSubscribersConfirmationText } else { deleteForAllConfirmation = strongSelf.presentationData.strings.ChatList_DeleteForAllMembersConfirmationText @@ -2854,7 +2850,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) } - public func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) { + public func maybeAskForPeerChatRemoval(peer: EngineRenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) { guard let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else { completion(false) return @@ -2866,10 +2862,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController canRemoveGlobally = true } } - if let user = chatPeer as? TelegramUser, user.botInfo != nil { + if case let .user(user) = chatPeer, user.botInfo != nil { canRemoveGlobally = false } - if let _ = chatPeer as? TelegramSecretChat { + if case .secretChat = chatPeer { canRemoveGlobally = true } @@ -2877,7 +2873,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let actionSheet = ActionSheetController(presentationData: self.presentationData) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) if joined || mainPeer.isDeleted { items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in @@ -2895,7 +2891,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) completion(true) })) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string, color: .destructive, action: { [weak self, weak actionSheet] in + items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() guard let strongSelf = self else { return @@ -3016,7 +3012,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) } - private func schedulePeerChatRemoval(peer: RenderedPeer, type: InteractiveMessagesDeletionType, deleteGloballyIfPossible: Bool, completion: @escaping () -> Void) { + private func schedulePeerChatRemoval(peer: EngineRenderedPeer, type: InteractiveMessagesDeletionType, deleteGloballyIfPossible: Bool, completion: @escaping () -> Void) { guard let chatPeer = peer.peers[peer.peerId] else { return } @@ -3035,7 +3031,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) self.chatListDisplayNode.containerNode.currentItemNode.setCurrentRemovingPeerId(nil) let statusText: String - if let channel = chatPeer as? TelegramChannel { + if case let .channel(channel) = chatPeer { if deleteGloballyIfPossible { if case .broadcast = channel.info { statusText = self.presentationData.strings.Undo_DeletedChannel @@ -3049,13 +3045,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController statusText = self.presentationData.strings.Undo_LeftGroup } } - } else if let _ = chatPeer as? TelegramGroup { + } else if case .legacyGroup = chatPeer { if deleteGloballyIfPossible { statusText = self.presentationData.strings.Undo_DeletedGroup } else { statusText = self.presentationData.strings.Undo_LeftGroup } - } else if let _ = chatPeer as? TelegramSecretChat { + } else if case .secretChat = chatPeer { statusText = self.presentationData.strings.Undo_SecretChatDeleted } else { if case .forEveryone = type { @@ -3078,7 +3074,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } if value == .commit { strongSelf.chatListDisplayNode.containerNode.currentItemNode.setCurrentRemovingPeerId(peerId) - if let channel = chatPeer as? TelegramChannel { + if case let .channel(channel) = chatPeer { strongSelf.context.peerChannelMemberCategoriesContextsManager.externallyRemoved(peerId: channel.id, memberId: strongSelf.context.account.peerId) } let _ = strongSelf.context.engine.peers.removePeerChat(peerId: peerId, reportChatSpam: false, deleteGloballyIfPossible: deleteGloballyIfPossible).start(completed: { diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index a5d4d91775..a59f088772 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -225,12 +225,12 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { case includePeersHeader(String) case addIncludePeer(title: String) case includeCategory(index: Int, category: ChatListFilterIncludeCategory, title: String, isRevealed: Bool) - case includePeer(index: Int, peer: RenderedPeer, isRevealed: Bool) + case includePeer(index: Int, peer: EngineRenderedPeer, isRevealed: Bool) case includePeerInfo(String) case excludePeersHeader(String) case addExcludePeer(title: String) case excludeCategory(index: Int, category: ChatListFilterExcludeCategory, title: String, isRevealed: Bool) - case excludePeer(index: Int, peer: RenderedPeer, isRevealed: Bool) + case excludePeer(index: Int, peer: EngineRenderedPeer, isRevealed: Bool) case excludePeerInfo(String) case includeExpand(String) case excludeExpand(String) @@ -389,7 +389,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { } ) case let .includePeer(_, peer, isRevealed): - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(peer.chatMainPeer!), height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: { + return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer.chatMainPeer!, height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: { arguments.deleteIncludePeer(peer.peerId) })]), switchValue: nil, enabled: true, selectable: false, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) }) @@ -397,7 +397,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { arguments.deleteIncludePeer(id) }) case let .excludePeer(_, peer, isRevealed): - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(peer.chatMainPeer!), height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: { + return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer.chatMainPeer!, height: .peerList, aliasHandling: .threatSelfAsSaved, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: isRevealed), revealOptions: ItemListPeerItemRevealOptions(options: [ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: { arguments.deleteExcludePeer(peer.peerId) })]), switchValue: nil, enabled: true, selectable: false, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in arguments.setItemIdWithRevealedOptions(lhs.flatMap { .peer($0) }, rhs.flatMap { .peer($0) }) @@ -454,7 +454,7 @@ private struct ChatListFilterPresetControllerState: Equatable { } } -private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, state: ChatListFilterPresetControllerState, includePeers: [RenderedPeer], excludePeers: [RenderedPeer]) -> [ChatListFilterPresetEntry] { +private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, state: ChatListFilterPresetControllerState, includePeers: [EngineRenderedPeer], excludePeers: [EngineRenderedPeer]) -> [ChatListFilterPresetEntry] { var entries: [ChatListFilterPresetEntry] = [] if isNewFilter { @@ -933,12 +933,12 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat } ) - let currentPeers = Atomic<[PeerId: RenderedPeer]>(value: [:]) + let currentPeers = Atomic<[PeerId: EngineRenderedPeer]>(value: [:]) let stateWithPeers = statePromise.get() - |> mapToSignal { state -> Signal<(ChatListFilterPresetControllerState, [RenderedPeer], [RenderedPeer]), NoError> in + |> mapToSignal { state -> Signal<(ChatListFilterPresetControllerState, [EngineRenderedPeer], [EngineRenderedPeer]), NoError> in let currentPeersValue = currentPeers.with { $0 } - var included: [RenderedPeer] = [] - var excluded: [RenderedPeer] = [] + var included: [EngineRenderedPeer] = [] + var excluded: [EngineRenderedPeer] = [] var missingPeers = false for peerId in state.additionallyIncludePeers { if let peer = currentPeersValue[peerId] { @@ -955,20 +955,30 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat } } if missingPeers { - return context.account.postbox.transaction { transaction -> (ChatListFilterPresetControllerState, [RenderedPeer], [RenderedPeer]) in - var included: [RenderedPeer] = [] - var excluded: [RenderedPeer] = [] - var allPeers: [PeerId: RenderedPeer] = [:] + return context.engine.data.get( + EngineDataMap( + state.additionallyIncludePeers.map { peerId -> TelegramEngine.EngineData.Item.Peer.RenderedPeer in + return TelegramEngine.EngineData.Item.Peer.RenderedPeer(id: peerId) + } + ), + EngineDataMap( + state.additionallyExcludePeers.map { peerId -> TelegramEngine.EngineData.Item.Peer.RenderedPeer in + return TelegramEngine.EngineData.Item.Peer.RenderedPeer(id: peerId) + } + ) + ) + |> map { additionallyIncludePeers, additionallyExcludePeers -> (ChatListFilterPresetControllerState, [EngineRenderedPeer], [EngineRenderedPeer]) in + var included: [EngineRenderedPeer] = [] + var excluded: [EngineRenderedPeer] = [] + var allPeers: [EnginePeer.Id: EngineRenderedPeer] = [:] for peerId in state.additionallyIncludePeers { - if let peer = transaction.getPeer(peerId) { - let renderedPeer = RenderedPeer(peer: peer) + if let renderedPeerValue = additionallyIncludePeers[peerId], let renderedPeer = renderedPeerValue { included.append(renderedPeer) allPeers[renderedPeer.peerId] = renderedPeer } } for peerId in state.additionallyExcludePeers { - if let peer = transaction.getPeer(peerId) { - let renderedPeer = RenderedPeer(peer: peer) + if let renderedPeerValue = additionallyExcludePeers[peerId], let renderedPeer = renderedPeerValue { excluded.append(renderedPeer) allPeers[renderedPeer.peerId] = renderedPeer } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 24e710de94..20059ce320 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -614,11 +614,16 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo guard let strongSelf = self, let messageIds = strongSelf.stateValue.selectedMessageIds, !messageIds.isEmpty else { return } - let _ = (strongSelf.context.account.postbox.transaction { transaction -> [EngineMessage] in + let _ = (strongSelf.context.engine.data.get(EngineDataMap( + messageIds.map { id -> TelegramEngine.EngineData.Item.Messages.Message in + return TelegramEngine.EngineData.Item.Messages.Message(id: id) + } + )) + |> map { messageMap -> [EngineMessage] in var messages: [EngineMessage] = [] for id in messageIds { - if let message = transaction.getMessage(id) { - messages.append(EngineMessage(message)) + if let messageValue = messageMap[id], let message = messageValue { + messages.append(message) } } return messages @@ -641,11 +646,16 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo guard let strongSelf = self, let messageIds = strongSelf.stateValue.selectedMessageIds, !messageIds.isEmpty else { return } - let _ = (strongSelf.context.account.postbox.transaction { transaction -> [EngineMessage] in + let _ = (strongSelf.context.engine.data.get(EngineDataMap( + messageIds.map { id -> TelegramEngine.EngineData.Item.Messages.Message in + return TelegramEngine.EngineData.Item.Messages.Message(id: id) + } + )) + |> map { messageMap -> [EngineMessage] in var messages: [EngineMessage] = [] for id in messageIds { - if let message = transaction.getMessage(id) { - messages.append(EngineMessage(message)) + if let messageValue = messageMap[id], let message = messageValue { + messages.append(message) } } return messages @@ -898,43 +908,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo }))) } - /*if !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty { - if !items.isEmpty { - items.append(.separator) - } - - if actions.options.contains(.deleteLocally) { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe, textColor: .destructive, icon: { _ in - return nil - }, action: { controller, f in - guard let strongSelf = self else { - return - } - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forLocalPeer).start() - f(.dismissWithoutContent) - }))) - } - - if actions.options.contains(.deleteGlobally) { - let text: String - if let mainPeer = message.peers[message.id.peerId] { - if mainPeer is TelegramUser { - text = strongSelf.presentationData.strings.ChatList_DeleteForEveryone(EnginePeer(mainPeer).compactDisplayTitle).string - } else { - text = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone - } - } else { - text = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone - } - items.append(.action(ContextMenuActionItem(text: text, textColor: .destructive, icon: { _ in - return nil - }, action: { controller, f in - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).start() - f(.dismissWithoutContent) - }))) - } - }*/ - return items } @@ -944,7 +917,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return } - let _ = storedMessageFromSearch(account: self.context.account, message: message._asMessage()).start() + self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [message]) var linkForCopying: String? var currentSupernode: ASDisplayNode? = node @@ -1093,8 +1066,19 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo if let messageIds = messageIds ?? self.stateValue.selectedMessageIds, !messageIds.isEmpty { if isDownloads { - let _ = (self.context.account.postbox.transaction { transaction -> [Message] in - return messageIds.compactMap(transaction.getMessage) + let _ = (self.context.engine.data.get(EngineDataMap( + messageIds.map { id -> TelegramEngine.EngineData.Item.Messages.Message in + return TelegramEngine.EngineData.Item.Messages.Message(id: id) + } + )) + |> map { messageMap -> [EngineMessage] in + var messages: [EngineMessage] = [] + for id in messageIds { + if let messageValue = messageMap[id], let message = messageValue { + messages.append(message) + } + } + return messages } |> deliverOnMainQueue).start(next: { [weak self] messages in guard let strongSelf = self else { @@ -1142,13 +1126,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo }) } else { let (peers, messages) = self.currentMessages - let _ = (self.context.account.postbox.transaction { transaction -> Void in - for id in messageIds { - if transaction.getMessage(id) == nil, let message = messages[id] { - storeMessageFromSearch(transaction: transaction, message: message._asMessage()) - } - } - }).start() + + self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: messages.values.filter { messageIds.contains($0.id) }) self.activeActionDisposable.set((self.context.sharedContext.chatAvailableMessageActions(postbox: self.context.account.postbox, accountPeerId: self.context.account.peerId, messageIds: messageIds, messages: messages, peers: peers) |> deliverOnMainQueue).start(next: { [weak self] actions in @@ -1211,13 +1190,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo let messageIds = messageIds ?? self.stateValue.selectedMessageIds if let messageIds = messageIds, !messageIds.isEmpty { let messages = self.paneContainerNode.allCurrentMessages() - let _ = (self.context.account.postbox.transaction { transaction -> Void in - for id in messageIds { - if transaction.getMessage(id) == nil, let message = messages[id] { - storeMessageFromSearch(transaction: transaction, message: message._asMessage()) - } - } - }).start() + + self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: messages.values.filter { messageIds.contains($0.id) }) let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true)) peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode, forwardOptions in diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 7fc4c538ee..abbdfcafd2 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -799,7 +799,7 @@ public struct ChatListSearchOptions { private struct DownloadItem: Equatable { let resourceId: MediaResourceId - let message: Message + let message: EngineMessage let priority: FetchManagerPriorityKey let isPaused: Bool @@ -1053,8 +1053,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { for entry in entries { switch entry.id.locationKey { case let .messageId(id): - itemSignals.append(context.account.postbox.transaction { transaction -> DownloadItem? in - if let message = transaction.getMessage(id) { + itemSignals.append(context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id)) + |> map { message -> DownloadItem? in + if let message = message { return DownloadItem(resourceId: entry.resourceReference.resource.id, message: message, priority: entry.priority, isPaused: entry.isPaused) } return nil @@ -1134,7 +1135,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } existingMessageIds.insert(item.message.id) - let message = EngineMessage(item.message) + let message = item.message if !queryTokens.isEmpty { if !messageMatchesTokens(message: message, tokens: queryTokens) { diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 7e5bd06193..e34bd134e7 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -55,7 +55,7 @@ public final class HashtagSearchController: TelegramBaseController { }, additionalCategorySelected: { _ in }, messageSelected: { [weak self] peer, message, _ in if let strongSelf = self { - strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer()) |> deliverOnMainQueue).start(next: { actualPeerId in + strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer) |> deliverOnMainQueue).start(next: { actualPeerId in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: actualPeerId), subject: message.id.peerId == actualPeerId ? .message(id: .id(message.id), highlight: true, timecode: nil) : nil, keepStack: .always)) } diff --git a/submodules/Postbox/Sources/MutableLocalNoticeEntryView.swift b/submodules/Postbox/Sources/MutableLocalNoticeEntryView.swift new file mode 100644 index 0000000000..66c0781086 --- /dev/null +++ b/submodules/Postbox/Sources/MutableLocalNoticeEntryView.swift @@ -0,0 +1,36 @@ +import Foundation + +final class MutableLocalNoticeEntryView: MutablePostboxView { + private let key: NoticeEntryKey + fileprivate var value: CodableEntry? + + init(postbox: PostboxImpl, key: NoticeEntryKey) { + self.key = key + self.value = postbox.noticeTable.get(key: key) + } + + func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { + var updated = false + if transaction.updatedNoticeEntryKeys.contains(self.key) { + self.value = postbox.noticeTable.get(key: key) + updated = true + } + return updated + } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } + + func immutableView() -> PostboxView { + return LocalNoticeEntryView(self) + } +} + +public final class LocalNoticeEntryView: PostboxView { + public let value: CodableEntry? + + init(_ view: MutableLocalNoticeEntryView) { + self.value = view.value + } +} diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index 9b7dbea1e9..ca93d1c418 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -33,6 +33,7 @@ public enum PostboxViewKey: Hashable { case topChatMessage(peerIds: [PeerId]) case contacts(accountPeerId: PeerId?, includePresences: Bool) case deletedMessages(peerId: PeerId) + case notice(key: NoticeEntryKey) public func hash(into hasher: inout Hasher) { switch self { @@ -109,6 +110,8 @@ public enum PostboxViewKey: Hashable { hasher.combine(16) case let .deletedMessages(peerId): hasher.combine(peerId) + case let .notice(key): + hasher.combine(key) } } @@ -306,6 +309,12 @@ public enum PostboxViewKey: Hashable { } else { return false } + case let .notice(key): + if case .notice(key) = rhs { + return true + } else { + return false + } } } } @@ -376,5 +385,7 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost return MutableContactPeersView(postbox: postbox, accountPeerId: accountPeerId, includePresences: includePresences) case let .deletedMessages(peerId): return MutableDeletedMessagesView(peerId: peerId) + case let .notice(key): + return MutableLocalNoticeEntryView(postbox: postbox, key: key) } } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 7b282e7966..c1a2d97a28 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -174,6 +174,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1460809483] = { return Api.Dialog.parse_dialog($0) } dict[1908216652] = { return Api.Dialog.parse_dialogFolder($0) } dict[1949890536] = { return Api.DialogFilter.parse_dialogFilter($0) } + dict[909284270] = { return Api.DialogFilter.parse_dialogFilterDefault($0) } dict[2004110666] = { return Api.DialogFilterSuggested.parse_dialogFilterSuggested($0) } dict[-445792507] = { return Api.DialogPeer.parse_dialogPeer($0) } dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) } @@ -426,6 +427,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1262252875] = { return Api.MessageAction.parse_messageActionWebViewDataSent($0) } dict[1205698681] = { return Api.MessageAction.parse_messageActionWebViewDataSentMe($0) } dict[546203849] = { return Api.MessageEntity.parse_inputMessageEntityMentionName($0) } + dict[1592721940] = { return Api.MessageEntity.parse_messageEntityAnimatedEmoji($0) } dict[1981704948] = { return Api.MessageEntity.parse_messageEntityBankCard($0) } dict[34469328] = { return Api.MessageEntity.parse_messageEntityBlockquote($0) } dict[-1117713463] = { return Api.MessageEntity.parse_messageEntityBold($0) } @@ -985,7 +987,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[946083368] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultSuccess($0) } dict[816245886] = { return Api.messages.Stickers.parse_stickers($0) } dict[-244016606] = { return Api.messages.Stickers.parse_stickersNotModified($0) } - dict[-1442723025] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) } + dict[-1077051894] = { return Api.messages.TranscribedAudio.parse_transcribedAudio($0) } dict[1741309751] = { return Api.messages.TranslatedText.parse_translateNoResult($0) } dict[-1575684144] = { return Api.messages.TranslatedText.parse_translateResultText($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } diff --git a/submodules/TelegramApi/Sources/Api11.swift b/submodules/TelegramApi/Sources/Api11.swift index 8172159a5c..0c3067154e 100644 --- a/submodules/TelegramApi/Sources/Api11.swift +++ b/submodules/TelegramApi/Sources/Api11.swift @@ -1,6 +1,7 @@ public extension Api { enum MessageEntity: TypeConstructorDescription { case inputMessageEntityMentionName(offset: Int32, length: Int32, userId: Api.InputUser) + case messageEntityAnimatedEmoji(offset: Int32, length: Int32) case messageEntityBankCard(offset: Int32, length: Int32) case messageEntityBlockquote(offset: Int32, length: Int32) case messageEntityBold(offset: Int32, length: Int32) @@ -31,6 +32,13 @@ public extension Api { serializeInt32(length, buffer: buffer, boxed: false) userId.serialize(buffer, true) break + case .messageEntityAnimatedEmoji(let offset, let length): + if boxed { + buffer.appendInt32(1592721940) + } + serializeInt32(offset, buffer: buffer, boxed: false) + serializeInt32(length, buffer: buffer, boxed: false) + break case .messageEntityBankCard(let offset, let length): if boxed { buffer.appendInt32(1981704948) @@ -174,6 +182,8 @@ public extension Api { switch self { case .inputMessageEntityMentionName(let offset, let length, let userId): return ("inputMessageEntityMentionName", [("offset", String(describing: offset)), ("length", String(describing: length)), ("userId", String(describing: userId))]) + case .messageEntityAnimatedEmoji(let offset, let length): + return ("messageEntityAnimatedEmoji", [("offset", String(describing: offset)), ("length", String(describing: length))]) case .messageEntityBankCard(let offset, let length): return ("messageEntityBankCard", [("offset", String(describing: offset)), ("length", String(describing: length))]) case .messageEntityBlockquote(let offset, let length): @@ -234,6 +244,20 @@ public extension Api { return nil } } + public static func parse_messageEntityAnimatedEmoji(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.MessageEntity.messageEntityAnimatedEmoji(offset: _1!, length: _2!) + } + else { + return nil + } + } public static func parse_messageEntityBankCard(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 2acb37d32a..8c14d1edfe 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -490,14 +490,15 @@ public extension Api.messages { } public extension Api.messages { enum TranscribedAudio: TypeConstructorDescription { - case transcribedAudio(text: String) + case transcribedAudio(transcriptionId: Int64, text: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .transcribedAudio(let text): + case .transcribedAudio(let transcriptionId, let text): if boxed { - buffer.appendInt32(-1442723025) + buffer.appendInt32(-1077051894) } + serializeInt64(transcriptionId, buffer: buffer, boxed: false) serializeString(text, buffer: buffer, boxed: false) break } @@ -505,17 +506,20 @@ public extension Api.messages { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .transcribedAudio(let text): - return ("transcribedAudio", [("text", String(describing: text))]) + case .transcribedAudio(let transcriptionId, let text): + return ("transcribedAudio", [("transcriptionId", String(describing: transcriptionId)), ("text", String(describing: text))]) } } public static func parse_transcribedAudio(_ reader: BufferReader) -> TranscribedAudio? { - var _1: String? - _1 = parseString(reader) + var _1: Int64? + _1 = reader.readInt64() + var _2: String? + _2 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.messages.TranscribedAudio.transcribedAudio(text: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.TranscribedAudio.transcribedAudio(transcriptionId: _1!, text: _2!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index fff5642c9c..98e4086e03 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -4963,6 +4963,24 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func rateTranscribedAudio(peer: Api.InputPeer, msgId: Int32, transcriptionId: Int64, good: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2132608815) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt64(transcriptionId, buffer: buffer, boxed: false) + good.serialize(buffer, true) + return (FunctionDescription(name: "messages.rateTranscribedAudio", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("transcriptionId", String(describing: transcriptionId)), ("good", String(describing: good))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.messages { static func readDiscussion(peer: Api.InputPeer, msgId: Int32, readMaxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 04dce18cf0..17edf43042 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -897,6 +897,7 @@ public extension Api { public extension Api { enum DialogFilter: TypeConstructorDescription { case dialogFilter(flags: Int32, id: Int32, title: String, emoticon: String?, pinnedPeers: [Api.InputPeer], includePeers: [Api.InputPeer], excludePeers: [Api.InputPeer]) + case dialogFilterDefault public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -923,6 +924,12 @@ public extension Api { for item in excludePeers { item.serialize(buffer, true) } + break + case .dialogFilterDefault: + if boxed { + buffer.appendInt32(909284270) + } + break } } @@ -931,6 +938,8 @@ public extension Api { switch self { case .dialogFilter(let flags, let id, let title, let emoticon, let pinnedPeers, let includePeers, let excludePeers): return ("dialogFilter", [("flags", String(describing: flags)), ("id", String(describing: id)), ("title", String(describing: title)), ("emoticon", String(describing: emoticon)), ("pinnedPeers", String(describing: pinnedPeers)), ("includePeers", String(describing: includePeers)), ("excludePeers", String(describing: excludePeers))]) + case .dialogFilterDefault: + return ("dialogFilterDefault", []) } } @@ -969,6 +978,9 @@ public extension Api { return nil } } + public static func parse_dialogFilterDefault(_ reader: BufferReader) -> DialogFilter? { + return Api.DialogFilter.dialogFilterDefault + } } } diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 6b2e662b9e..54465d7063 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -336,44 +336,46 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes var result: [MessageTextEntity] = [] for entity in entities { switch entity { - case .messageEntityUnknown, .inputMessageEntityMentionName: - break - case let .messageEntityMention(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Mention)) - case let .messageEntityHashtag(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Hashtag)) - case let .messageEntityBotCommand(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BotCommand)) - case let .messageEntityUrl(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Url)) - case let .messageEntityEmail(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Email)) - case let .messageEntityBold(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Bold)) - case let .messageEntityItalic(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) - case let .messageEntityCode(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) - case let .messageEntityPre(offset, length, _): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) - case let .messageEntityTextUrl(offset, length, url): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) - case let .messageEntityMentionName(offset, length, userId): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextMention(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))))) - case let .messageEntityPhone(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .PhoneNumber)) - case let .messageEntityCashtag(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Hashtag)) - case let .messageEntityUnderline(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Underline)) - case let .messageEntityStrike(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Strikethrough)) - case let .messageEntityBlockquote(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BlockQuote)) - case let .messageEntityBankCard(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BankCard)) - case let .messageEntitySpoiler(offset, length): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Spoiler)) + case .messageEntityUnknown, .inputMessageEntityMentionName: + break + case let .messageEntityMention(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Mention)) + case let .messageEntityHashtag(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Hashtag)) + case let .messageEntityBotCommand(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BotCommand)) + case let .messageEntityUrl(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Url)) + case let .messageEntityEmail(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Email)) + case let .messageEntityBold(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Bold)) + case let .messageEntityItalic(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) + case let .messageEntityCode(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) + case let .messageEntityPre(offset, length, _): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityTextUrl(offset, length, url): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) + case let .messageEntityMentionName(offset, length, userId): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextMention(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))))) + case let .messageEntityPhone(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .PhoneNumber)) + case let .messageEntityCashtag(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Hashtag)) + case let .messageEntityUnderline(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Underline)) + case let .messageEntityStrike(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Strikethrough)) + case let .messageEntityBlockquote(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BlockQuote)) + case let .messageEntityBankCard(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BankCard)) + case let .messageEntitySpoiler(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Spoiler)) + case let .messageEntityAnimatedEmoji(offset, length): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .AnimatedEmoji)) } } return result diff --git a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift index d3e1a625ec..bc25b0d370 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift @@ -48,6 +48,8 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa apiEntities.append(.messageEntityBankCard(offset: offset, length: length)) case .Spoiler: apiEntities.append(.messageEntitySpoiler(offset: offset, length: length)) + case .AnimatedEmoji: + apiEntities.append(.messageEntityAnimatedEmoji(offset: offset, length: length)) case .Custom: break } diff --git a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift index 747edd5b11..aa1e63dd32 100644 --- a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift +++ b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift @@ -142,7 +142,7 @@ private func findMediaResource(media: Media, previousMedia: Media?, resource: Me return nil } -public func findMediaResourceById(message: Message, resourceId: MediaResourceId) -> TelegramMediaResource? { +public func findMediaResourceById(message: EngineMessage, resourceId: MediaResourceId) -> TelegramMediaResource? { for media in message.media { if let result = findMediaResourceById(media: media, resourceId: resourceId) { return result diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index a1d402afd4..46877da46c 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -709,6 +709,8 @@ private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi break case .Spoiler: break + case .AnimatedEmoji: + break case .Custom: break } @@ -760,6 +762,8 @@ private func decryptedEntities101(_ entities: [MessageTextEntity]?) -> [SecretAp break case .Spoiler: break + case .AnimatedEmoji: + break case .Custom: break } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index e9ab11ad0a..4807303a57 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -21,6 +21,7 @@ public enum MessageTextEntityType: Equatable { case Underline case BankCard case Spoiler + case AnimatedEmoji case Custom(type: CustomEntityType) } @@ -71,6 +72,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { self.type = .BankCard case 17: self.type = .Spoiler + case 18: + self.type = .AnimatedEmoji case Int32.max: self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0)) default: @@ -126,6 +129,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { self.type = .BankCard case 17: self.type = .Spoiler + case 18: + self.type = .AnimatedEmoji case Int32.max: let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0 self.type = .Custom(type: customType) @@ -176,6 +181,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { encoder.encodeInt32(16, forKey: "_rawValue") case .Spoiler: encoder.encodeInt32(17, forKey: "_rawValue") + case .AnimatedEmoji: + encoder.encodeInt32(18, forKey: "_rawValue") case let .Custom(type): encoder.encodeInt32(Int32.max, forKey: "_rawValue") encoder.encodeInt32(type, forKey: "type") @@ -226,6 +233,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { try container.encode(16 as Int32, forKey: "_rawValue") case .Spoiler: try container.encode(17 as Int32, forKey: "_rawValue") + case .AnimatedEmoji: + try container.encode(18 as Int32, forKey: "_rawValue") case let .Custom(type): try container.encode(Int32.max as Int32, forKey: "_rawValue") try container.encode(type as Int32, forKey: "type") diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift index 4efd91fe1d..5d83c3f397 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/Configuration.swift @@ -166,5 +166,27 @@ public extension TelegramEngine.EngineData.Item { return EngineConfiguration.UserLimits(UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: self.isPremium)) } } + + public struct SuggestedLocalization: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = SuggestedLocalizationEntry? + + public init() { + } + + var key: PostboxViewKey { + return .preferences(keys: Set([PreferencesKeys.suggestedLocalization])) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? PreferencesView else { + preconditionFailure() + } + guard let suggestedLocalization = view.values[PreferencesKeys.suggestedLocalization]?.get(SuggestedLocalizationEntry.self) else { + return nil + } + return suggestedLocalization + } + } + } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/Messages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/Messages.swift index 6229bb1985..1f39e9aede 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/Messages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/Messages.swift @@ -54,10 +54,14 @@ public extension EnginePeerReadCounters { public extension TelegramEngine.EngineData.Item { enum Messages { - public struct Message: TelegramEngineDataItem, PostboxViewDataItem { + public struct Message: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = Optional fileprivate var id: EngineMessage.Id + + public var mapKey: EngineMessage.Id { + return self.id + } public init(id: EngineMessage.Id) { self.id = id diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/Notices.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/Notices.swift new file mode 100644 index 0000000000..250d223a6a --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/Notices.swift @@ -0,0 +1,27 @@ +import SwiftSignalKit +import Postbox + +public extension TelegramEngine.EngineData.Item { + enum Notices { + public struct Notice: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = CodableEntry? + + private let entryKey: NoticeEntryKey + + public init(key: NoticeEntryKey) { + self.entryKey = key + } + + var key: PostboxViewKey { + return .notice(key: self.entryKey) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? LocalNoticeEntryView else { + preconditionFailure() + } + return view.value + } + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift index aa94a11570..2bc77abfbc 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift @@ -32,7 +32,7 @@ public extension TelegramEngine.EngineData.Item { } } - public struct RenderedPeer: TelegramEngineDataItem, PostboxViewDataItem { + public struct RenderedPeer: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = Optional fileprivate var id: EnginePeer.Id diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 82c63a1b8e..bf5eda9925 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -322,7 +322,7 @@ public extension TelegramEngine { return _internal_translate(network: self.account.network, text: text, fromLang: fromLang, toLang: toLang) } - public func transcribeAudio(messageId: MessageId) -> Signal { + public func transcribeAudio(messageId: MessageId) -> Signal { return _internal_transcribeAudio(postbox: self.account.postbox, network: self.account.network, messageId: messageId) } @@ -354,5 +354,13 @@ public extension TelegramEngine { public func attachMenuBots() -> Signal<[AttachMenuBot], NoError> { return _internal_attachMenuBots(postbox: self.account.postbox) } + + public func ensureMessagesAreLocallyAvailable(messages: [EngineMessage]) { + let _ = self.account.postbox.transaction({ transaction in + for message in messages { + _internal_storeMessageFromSearch(transaction: transaction, message: message._asMessage()) + } + }).start() + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift index b484e669fe..0b4f467de1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift @@ -29,11 +29,16 @@ func _internal_translate(network: Network, text: String, fromLang: String?, toLa } } -func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal { +public struct EngineAudioTranscriptionResult { + public var id: Int64 + public var text: String +} + +func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal { return postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) } - |> mapToSignal { inputPeer -> Signal in + |> mapToSignal { inputPeer -> Signal in guard let inputPeer = inputPeer else { return .single(nil) } @@ -42,13 +47,13 @@ func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: Me |> `catch` { _ -> Signal in return .single(nil) } - |> mapToSignal { result -> Signal in + |> mapToSignal { result -> Signal in guard let result = result else { return .single(nil) } switch result { - case let .transcribedAudio(string): - return .single(string) + case let .transcribedAudio(transcriptionId, text): + return .single(EngineAudioTranscriptionResult(id: transcriptionId, text: text)) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift index 22668d0803..16bfdc4530 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift @@ -336,6 +336,8 @@ extension ChatListFilter { } ) ) + case .dialogFilterDefault: + preconditionFailure() } } @@ -465,6 +467,8 @@ private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, net } } } + case .dialogFilterDefault: + break } } return (filters, missingPeers, missingChats) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index f359f6f588..c2c2a2dddf 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -718,6 +718,10 @@ public extension TelegramEngine { public func deleteNotificationSound(fileId: Int64) -> Signal { return _internal_deleteNotificationSound(account: self.account, fileId: fileId) } + + public func ensurePeerIsLocallyAvailable(peer: EnginePeer) -> Signal { + return _internal_storedMessageFromSearchPeer(account: self.account, peer: peer._asPeer()) + } } } diff --git a/submodules/AccountContext/Sources/StoredMessageFromSearchPeer.swift b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift similarity index 52% rename from submodules/AccountContext/Sources/StoredMessageFromSearchPeer.swift rename to submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift index 8cf71a3dbd..0271d119c9 100644 --- a/submodules/AccountContext/Sources/StoredMessageFromSearchPeer.swift +++ b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift @@ -1,9 +1,8 @@ import Foundation import Postbox -import TelegramCore import SwiftSignalKit -public func storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal { +func _internal_storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal { return account.postbox.transaction { transaction -> PeerId in if transaction.getPeer(peer.id) == nil { updatePeers(transaction: transaction, peers: [peer], update: { previousPeer, updatedPeer in @@ -17,25 +16,7 @@ public func storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal< } } -public func storedMessageFromSearch(account: Account, message: Message) -> Signal { - return account.postbox.transaction { transaction -> Void in - if transaction.getMessage(message.id) == nil { - for (_, peer) in message.peers { - if transaction.getPeer(peer.id) == nil { - updatePeers(transaction: transaction, peers: [peer], update: { previousPeer, updatedPeer in - return updatedPeer - }) - } - } - - let storeMessage = StoreMessage(id: .Id(message.id), globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: StoreMessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media) - - let _ = transaction.addMessages([storeMessage], location: .Random) - } - } -} - -public func storeMessageFromSearch(transaction: Transaction, message: Message) { +func _internal_storeMessageFromSearch(transaction: Transaction, message: Message) { if transaction.getMessage(message.id) == nil { for (_, peer) in message.peers { if transaction.getPeer(peer.id) == nil { diff --git a/submodules/TelegramUI/Components/AudioWaveformComponent/Sources/AudioWaveformComponent.swift b/submodules/TelegramUI/Components/AudioWaveformComponent/Sources/AudioWaveformComponent.swift index 37ee5e96f7..9115cf716c 100644 --- a/submodules/TelegramUI/Components/AudioWaveformComponent/Sources/AudioWaveformComponent.swift +++ b/submodules/TelegramUI/Components/AudioWaveformComponent/Sources/AudioWaveformComponent.swift @@ -13,6 +13,7 @@ public final class AudioWaveformComponent: Component { public let samples: Data public let peak: Int32 public let status: Signal + public let seek: (Double) -> Void public init( backgroundColor: UIColor, @@ -20,7 +21,8 @@ public final class AudioWaveformComponent: Component { shimmerColor: UIColor?, samples: Data, peak: Int32, - status: Signal + status: Signal, + seek: @escaping (Double) -> Void ) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor @@ -28,6 +30,7 @@ public final class AudioWaveformComponent: Component { self.samples = samples self.peak = peak self.status = status + self.seek = seek } public static func ==(lhs: AudioWaveformComponent, rhs: AudioWaveformComponent) -> Bool { @@ -49,7 +52,7 @@ public final class AudioWaveformComponent: Component { return true } - public final class View: UIView { + public final class View: UIView, UIGestureRecognizerDelegate { private struct ShimmerParams: Equatable { var backgroundColor: UIColor var foregroundColor: UIColor @@ -147,17 +150,39 @@ public final class AudioWaveformComponent: Component { return LayerImpl.self } + private var panRecognizer: UIPanGestureRecognizer? + + private var endScrubbing: ((Bool) -> Void)? + private var updateScrubbing: ((CGFloat, Double) -> Void)? + private var updateMultiplier: ((Double) -> Void)? + + private var verticalPanEnabled = false + + private var scrubbingMultiplier: Double = 1.0 + private var scrubbingStartLocation: CGPoint? + private var component: AudioWaveformComponent? private var validSize: CGSize? private var playbackStatus: MediaPlayerStatus? + private var scrubbingBeginTimestamp: Double? private var scrubbingTimestampValue: Double? + private var isAwaitingScrubbingApplication: Bool = false private var statusDisposable: Disposable? private var playbackStatusAnimator: ConstantDisplayLinkAnimator? private var revealProgress: CGFloat = 1.0 private var animator: DisplayLinkAnimator? + public var enableScrubbing: Bool = false { + didSet { + if self.enableScrubbing != oldValue { + self.disablesInteractiveTransitionGestureRecognizer = self.enableScrubbing + self.panRecognizer?.isEnabled = self.enableScrubbing + } + } + } + override init(frame: CGRect) { super.init(frame: frame) @@ -170,6 +195,12 @@ public final class AudioWaveformComponent: Component { (self.layer as! LayerImpl).didExitHierarchy = { [weak self] in self?.updatePlaybackAnimation() } + + let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) + panRecognizer.delegate = self + self.addGestureRecognizer(panRecognizer) + panRecognizer.isEnabled = false + self.panRecognizer = panRecognizer } required public init?(coder: NSCoder) { @@ -180,6 +211,86 @@ public final class AudioWaveformComponent: Component { self.statusDisposable?.dispose() } + @objc private func panGesture(_ recognizer: UIPanGestureRecognizer) { + var location = recognizer.location(in: self) + location.x -= self.bounds.minX + switch recognizer.state { + case .began: + self.scrubbingStartLocation = location + self.beginScrubbing() + case .changed: + if let scrubbingStartLocation = self.scrubbingStartLocation { + let delta = location.x - scrubbingStartLocation.x + var multiplier: Double = 1.0 + var skipUpdate = false + if self.verticalPanEnabled, location.y > scrubbingStartLocation.y { + let verticalDelta = abs(location.y - scrubbingStartLocation.y) + if verticalDelta > 150.0 { + multiplier = 0.01 + } else if verticalDelta > 100.0 { + multiplier = 0.25 + } else if verticalDelta > 50.0 { + multiplier = 0.5 + } + if multiplier != self.scrubbingMultiplier { + skipUpdate = true + self.scrubbingMultiplier = multiplier + self.scrubbingStartLocation = CGPoint(x: location.x, y: scrubbingStartLocation.y) + self.updateMultiplier?(multiplier) + } + } + if !skipUpdate { + self.updateScrubbing(addedFraction: delta / self.bounds.size.width, multiplier: multiplier) + } + } + case .ended, .cancelled: + if let scrubbingStartLocation = self.scrubbingStartLocation { + self.scrubbingStartLocation = nil + let delta = location.x - scrubbingStartLocation.x + self.updateScrubbing?(delta / self.bounds.size.width, self.scrubbingMultiplier) + self.endScrubbing(apply: recognizer.state == .ended) + //self.highlighted?(false) + self.scrubbingMultiplier = 1.0 + } + default: + break + } + } + + private func beginScrubbing() { + if let statusValue = self.playbackStatus, statusValue.duration > 0.0 { + self.scrubbingBeginTimestamp = statusValue.timestamp + self.scrubbingTimestampValue = statusValue.timestamp + self.setNeedsDisplay() + } + } + + private func endScrubbing(apply: Bool) { + self.scrubbingBeginTimestamp = nil + let scrubbingTimestampValue = self.scrubbingTimestampValue + + self.isAwaitingScrubbingApplication = true + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in + guard let strongSelf = self, strongSelf.isAwaitingScrubbingApplication else { + return + } + strongSelf.isAwaitingScrubbingApplication = false + strongSelf.scrubbingTimestampValue = nil + strongSelf.setNeedsDisplay() + }) + + if let scrubbingTimestampValue = scrubbingTimestampValue, apply { + self.component?.seek(scrubbingTimestampValue) + } + } + + private func updateScrubbing(addedFraction: CGFloat, multiplier: Double) { + if let statusValue = self.playbackStatus, let scrubbingBeginTimestamp = self.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { + self.scrubbingTimestampValue = scrubbingBeginTimestamp + (statusValue.duration * Double(addedFraction)) * multiplier + self.setNeedsDisplay() + } + } + public func animateIn() { if self.animator == nil { self.revealProgress = 0.0 @@ -226,6 +337,12 @@ public final class AudioWaveformComponent: Component { guard let strongSelf = self else { return } + + if strongSelf.isAwaitingScrubbingApplication, value.duration > 0.0, let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue, abs(value.timestamp - scrubbingTimestampValue) <= value.duration * 0.01 { + strongSelf.isAwaitingScrubbingApplication = false + strongSelf.scrubbingTimestampValue = nil + } + if strongSelf.playbackStatus != value { strongSelf.playbackStatus = value strongSelf.setNeedsDisplay() @@ -439,6 +556,7 @@ public final class AudioWaveformComponent: Component { }*/ context.setFillColor(component.backgroundColor.mixedWith(component.foregroundColor, alpha: colorMixFraction).cgColor) + context.setBlendMode(.copy) let adjustedSampleHeight = sampleHeight - diff if adjustedSampleHeight.isLessThanOrEqualTo(sampleWidth) { @@ -450,9 +568,9 @@ public final class AudioWaveformComponent: Component { width: sampleWidth, height: adjustedSampleHeight - halfSampleWidth ) - context.fill(adjustedRect) context.fillEllipse(in: CGRect(x: adjustedRect.minX, y: adjustedRect.minY - halfSampleWidth, width: sampleWidth, height: sampleWidth)) context.fillEllipse(in: CGRect(x: adjustedRect.minX, y: adjustedRect.maxY - halfSampleWidth, width: sampleWidth, height: sampleWidth)) + context.fill(adjustedRect) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 6c72204aaa..86f7254fd9 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -368,7 +368,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } strongSelf.transcribeDisposable = nil strongSelf.audioTranscriptionState = .expanded - strongSelf.transcribedText = result + strongSelf.transcribedText = result?.text strongSelf.requestUpdateLayout(true) }) } @@ -962,7 +962,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { shimmerColor: isTranscriptionInProgress ? messageTheme.mediaActiveControlColor : nil, samples: audioWaveform?.samples ?? Data(), peak: audioWaveform?.peak ?? 0, - status: strongSelf.playbackStatus.get() + status: strongSelf.playbackStatus.get(), + seek: { timestamp in + if let strongSelf = self, let context = strongSelf.context, let message = strongSelf.message, let type = peerMessageMediaPlayerType(message) { + context.sharedContext.mediaManager.playlistControl(.seek(timestamp), type: type) + } + } )), environment: {}, containerSize: scrubbingFrame.size @@ -1249,7 +1254,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if self.message?.forwardInfo != nil { fetchStatus = resourceStatus.fetchStatus } + (self.waveformView?.componentView as? AudioWaveformComponent.View)?.enableScrubbing = false //self.waveformScrubbingNode?.enableScrubbing = false + switch fetchStatus { case let .Fetching(_, progress): let adjustedProgress = max(progress, 0.027) @@ -1283,7 +1290,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } } case let .playbackStatus(playbackStatus): + (self.waveformView?.componentView as? AudioWaveformComponent.View)?.enableScrubbing = true //self.waveformScrubbingNode?.enableScrubbing = true + switch playbackStatus { case .playing: state = .pause diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 92af67dd79..bcf573f800 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1846,7 +1846,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate guard let strongSelf = self, let node = node as? ContextExtractedContentContainingNode else { return } - let _ = storedMessageFromSearch(account: strongSelf.context.account, message: message).start() + + strongSelf.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(message)]) var linkForCopying: String? var currentSupernode: ASDisplayNode? = node @@ -2196,11 +2197,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate var foundGalleryMessage: Message? if let searchContentNode = strongSelf.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode { if let galleryMessage = searchContentNode.messageForGallery(message.id) { - let _ = (strongSelf.context.account.postbox.transaction { transaction -> Void in - if transaction.getMessage(galleryMessage.id) == nil { - storeMessageFromSearch(transaction: transaction, message: galleryMessage) - } - }).start() + strongSelf.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(galleryMessage)]) foundGalleryMessage = galleryMessage } } @@ -3200,11 +3197,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate var foundGalleryMessage: Message? if let searchContentNode = self.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode { if let galleryMessage = searchContentNode.messageForGallery(id) { - let _ = (self.context.account.postbox.transaction { transaction -> Void in - if transaction.getMessage(galleryMessage.id) == nil { - storeMessageFromSearch(transaction: transaction, message: galleryMessage) - } - }).start() + self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(galleryMessage)]) foundGalleryMessage = galleryMessage } } @@ -5754,7 +5747,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } for childController in tabController.controllers { if let chatListController = childController as? ChatListController { - chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in + chatListController.maybeAskForPeerChatRemoval(peer: EngineRenderedPeer(peer: EnginePeer(peer)), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in if deleted { navigationController?.popToRoot(animated: true) }