Refactoring

This commit is contained in:
Ali 2021-09-23 18:59:10 +03:00
parent dd19880091
commit 3eead87254
79 changed files with 2106 additions and 1005 deletions

View File

@ -613,8 +613,8 @@ public protocol SharedAccountContext: AnyObject {
func navigateToChatController(_ params: NavigateToChatControllerParams)
func openLocationScreen(context: AccountContext, messageId: MessageId, navigationController: NavigationController)
func openExternalUrl(context: AccountContext, urlContext: OpenURLContext, url: String, forceExternal: Bool, presentationData: PresentationData, navigationController: NavigationController?, dismissInput: @escaping () -> Void)
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>) -> Signal<ChatAvailableMessageActions, NoError>
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>, messages: [MessageId: Message], peers: [PeerId: Peer]) -> Signal<ChatAvailableMessageActions, NoError>
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>) -> Signal<ChatAvailableMessageActions, NoError>
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>, messages: [EngineMessage.Id: EngineMessage], peers: [EnginePeer.Id: EnginePeer]) -> Signal<ChatAvailableMessageActions, NoError>
func resolveUrl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolvedUrl, NoError>
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?)
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)

View File

@ -1,6 +1,5 @@
import Foundation
import UIKit
import Postbox
import TelegramCore
import Display
import SwiftSignalKit

View File

@ -1,7 +1,6 @@
import Foundation
import UIKit
import SwiftSignalKit
import Postbox
import Display
import ImageIO
import TelegramCore
@ -85,10 +84,10 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?,
}
}
public func peerAvatarCompleteImage(account: Account, peer: Peer, size: CGSize, round: Bool = true, font: UIFont = avatarPlaceholderFont(size: 13.0), drawLetters: Bool = true, fullSize: Bool = false, blurred: Bool = false) -> Signal<UIImage?, NoError> {
public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, size: CGSize, round: Bool = true, font: UIFont = avatarPlaceholderFont(size: 13.0), drawLetters: Bool = true, fullSize: Bool = false, blurred: Bool = false) -> Signal<UIImage?, NoError> {
let iconSignal: Signal<UIImage?, NoError>
if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, round: round, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) {
if fullSize, let fullSizeSignal = peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) {
if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, round: round, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) {
if fullSize, let fullSizeSignal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), 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<UIImage?, NoError> in
if let fullSizeImage = fullSizeImage {

View File

@ -1,17 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatHistoryImportTasks",
module_name = "ChatHistoryImportTasks",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,4 +0,0 @@
import Foundation
public enum ChatHistoryImportTasks {
}

View File

@ -22,7 +22,6 @@ swift_library(
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/RadialStatusNode:RadialStatusNode",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/ChatHistoryImportTasks:ChatHistoryImportTasks",
"//submodules/MimeTypes:MimeTypes",
"//submodules/ConfettiEffect:ConfettiEffect",
"//submodules/TelegramUniversalVideoContent:TelegramUniversalVideoContent",

View File

@ -559,7 +559,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
override public func loadDisplayNode() {
self.displayNode = ChatListControllerNode(context: self.context, groupId: self.groupId, filter: self.filter, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, controller: self)
self.displayNode = ChatListControllerNode(context: self.context, groupId: EngineChatList.Group(self.groupId), filter: self.filter, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, controller: self)
self.chatListDisplayNode.navigationBar = self.navigationBar
@ -656,7 +656,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.chatListDisplayNode.containerNode.groupSelected = { [weak self] groupId in
if let strongSelf = self {
if let navigationController = strongSelf.navigationController as? NavigationController {
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId, controlsHistoryPreload: false, enableDebugActions: false)
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId._asGroup(), controlsHistoryPreload: false, enableDebugActions: false)
chatListController.navigationPresentation = .master
navigationController.pushViewController(chatListController)
strongSelf.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true)
@ -683,7 +683,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)
strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer())
|> deliverOnMainQueue).start(next: { [weak strongSelf] actualPeerId in
if let strongSelf = strongSelf {
if let navigationController = strongSelf.navigationController as? NavigationController {
@ -707,7 +707,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
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], update: { previousPeer, updatedPeer in
updatePeers(transaction: transaction, peers: [peer._asPeer()], update: { previousPeer, updatedPeer in
return updatedPeer
})
}
@ -837,9 +837,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
switch item.content {
case let .groupReference(groupId, _, _, _, _):
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId._asGroup(), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
chatListController.navigationPresentation = .master
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId, chatListController: strongSelf) |> map { ContextController.Items(items: $0) }, gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(items: $0) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _):
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))

View File

@ -2,7 +2,6 @@ import Foundation
import UIKit
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import SwiftSignalKit
import TelegramPresentationData
@ -178,16 +177,40 @@ private final class ChatListShimmerNode: ASDisplayNode {
let chatListPresentationData = ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true)
let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let timestamp1: Int32 = 100000
let peers = SimpleDictionary<PeerId, Peer>()
let peers: [EnginePeer.Id: EnginePeer] = [:]
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in
gesture?.cancel()
}, present: { _ in })
let items = (0 ..< 2).map { _ -> ChatListItem in
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Text", attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
let message = EngineMessage(
stableId: 0,
stableVersion: 0,
id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0),
globallyUniqueId: nil,
groupingKey: nil,
groupInfo: nil,
threadId: nil,
timestamp: timestamp1,
flags: [],
tags: [],
globalTags: [],
localTags: [],
forwardInfo: nil,
author: peer1,
text: "Text",
attributes: [],
media: [],
peers: peers,
associatedMessages: [:],
associatedMessageIds: []
)
let readState = EnginePeerReadCounters()
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
}
var itemNodes: [ChatListItemNode] = []
@ -268,7 +291,7 @@ private final class ChatListContainerItemNode: ASDisplayNode {
private var validLayout: (CGSize, UIEdgeInsets, CGFloat)?
init(context: AccountContext, groupId: PeerGroupId, filter: ChatListFilter?, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void) {
init(context: AccountContext, groupId: EngineChatList.Group, filter: ChatListFilter?, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void) {
self.context = context
self.presentationData = presentationData
self.becameEmpty = becameEmpty
@ -396,7 +419,7 @@ private final class ChatListContainerItemNode: ASDisplayNode {
final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
private let context: AccountContext
private let groupId: PeerGroupId
private let groupId: EngineChatList.Group
private let previewing: Bool
private let controlsHistoryPreload: Bool
private let filterBecameEmpty: (ChatListFilter?) -> Void
@ -525,18 +548,18 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
var presentAlert: ((String) -> Void)?
var present: ((ViewController) -> Void)?
var toggleArchivedFolderHiddenByDefault: (() -> Void)?
var hidePsa: ((PeerId) -> Void)?
var deletePeerChat: ((PeerId, Bool) -> Void)?
var peerSelected: ((Peer, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
var groupSelected: ((PeerGroupId) -> Void)?
var updatePeerGrouping: ((PeerId, Bool) -> Void)?
var hidePsa: ((EnginePeer.Id) -> Void)?
var deletePeerChat: ((EnginePeer.Id, Bool) -> Void)?
var peerSelected: ((EnginePeer, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
var groupSelected: ((EngineChatList.Group) -> Void)?
var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
var contentScrollingEnded: ((ListView) -> Bool)?
var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?) -> Void)?
var addedVisibleChatsWithPeerIds: (([PeerId]) -> Void)?
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
var didBeginSelectingChats: (() -> Void)?
init(context: AccountContext, groupId: PeerGroupId, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void) {
init(context: AccountContext, groupId: EngineChatList.Group, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void) {
self.context = context
self.groupId = groupId
self.previewing = previewing
@ -983,7 +1006,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
final class ChatListControllerNode: ASDisplayNode {
private let context: AccountContext
private let groupId: PeerGroupId
private let groupId: EngineChatList.Group
private var presentationData: PresentationData
let containerNode: ChatListContainerNode
@ -1005,11 +1028,11 @@ final class ChatListControllerNode: ASDisplayNode {
private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat)?
var requestDeactivateSearch: (() -> Void)?
var requestOpenPeerFromSearch: ((Peer, Bool) -> Void)?
var requestOpenRecentPeerOptions: ((Peer) -> Void)?
var requestOpenMessageFromSearch: ((Peer, MessageId, Bool) -> Void)?
var requestOpenPeerFromSearch: ((EnginePeer, Bool) -> Void)?
var requestOpenRecentPeerOptions: ((EnginePeer) -> Void)?
var requestOpenMessageFromSearch: ((EnginePeer, EngineMessage.Id, Bool) -> Void)?
var requestAddContact: ((String) -> Void)?
var peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
var peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
var dismissSelfIfCompletedPresentation: (() -> Void)?
var isEmptyUpdated: ((Bool) -> Void)?
var emptyListAction: (() -> Void)?
@ -1017,7 +1040,7 @@ final class ChatListControllerNode: ASDisplayNode {
let debugListView = ListView()
init(context: AccountContext, groupId: PeerGroupId, filter: ChatListFilter?, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, controller: ChatListControllerImpl) {
init(context: AccountContext, groupId: EngineChatList.Group, filter: ChatListFilter?, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, controller: ChatListControllerImpl) {
self.context = context
self.groupId = groupId
self.presentationData = presentationData
@ -1051,7 +1074,7 @@ final class ChatListControllerNode: ASDisplayNode {
guard let strongSelf = self else {
return
}
if case .group = strongSelf.groupId {
if case .archive = strongSelf.groupId {
strongSelf.dismissSelfIfCompletedPresentation?()
}
}

View File

@ -1,7 +1,6 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Postbox
import Display
import SwiftSignalKit
import TelegramCore
@ -14,13 +13,13 @@ class ChatListRecentPeersListItem: ListViewItem {
let theme: PresentationTheme
let strings: PresentationStrings
let context: AccountContext
let peers: [Peer]
let peerSelected: (Peer) -> Void
let peerContextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void
let peers: [EnginePeer]
let peerSelected: (EnginePeer) -> Void
let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?) -> Void
let header: ListViewItemHeader?
init(theme: PresentationTheme, strings: PresentationStrings, context: AccountContext, peers: [Peer], peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) {
init(theme: PresentationTheme, strings: PresentationStrings, context: AccountContext, peers: [EnginePeer], peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void) {
self.theme = theme
self.strings = strings
self.context = context
@ -122,9 +121,9 @@ class ChatListRecentPeersListItemNode: ListViewItemNode {
peersNode.updateThemeAndStrings(theme: item.theme, strings: item.strings)
} else {
peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list, strings: item.strings, peerSelected: { peer in
self?.item?.peerSelected(peer._asPeer())
self?.item?.peerSelected(peer)
}, peerContextAction: { peer, node, gesture in
self?.item?.peerContextAction(peer._asPeer(), node, gesture)
self?.item?.peerContextAction(peer, node, gesture)
}, isPeerSelected: { _ in
return false
})
@ -161,7 +160,7 @@ class ChatListRecentPeersListItemNode: ListViewItemNode {
}
}
func viewAndPeerAtPoint(_ point: CGPoint) -> (UIView, PeerId)? {
func viewAndPeerAtPoint(_ point: CGPoint) -> (UIView, EnginePeer.Id)? {
if let peersNode = self.peersNode {
let adjustedLocation = self.convert(point, to: peersNode)
if let result = peersNode.viewAndPeerAtPoint(adjustedLocation) {

View File

@ -3,7 +3,6 @@ import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
@ -38,21 +37,21 @@ private enum ChatListTokenId: Int32 {
}
final class ChatListSearchInteraction {
let openPeer: (Peer, Peer?, Bool) -> Void
let openDisabledPeer: (Peer) -> Void
let openMessage: (Peer, MessageId, Bool) -> Void
let openPeer: (EnginePeer, EnginePeer?, Bool) -> Void
let openDisabledPeer: (EnginePeer) -> Void
let openMessage: (EnginePeer, EngineMessage.Id, Bool) -> Void
let openUrl: (String) -> Void
let clearRecentSearch: () -> Void
let addContact: (String) -> Void
let toggleMessageSelection: (MessageId, Bool) -> Void
let messageContextAction: ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void)
let mediaMessageContextAction: ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void)
let peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
let toggleMessageSelection: (EngineMessage.Id, Bool) -> Void
let messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void)
let mediaMessageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void)
let peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
let present: (ViewController, Any?) -> Void
let dismissInput: () -> Void
let getSelectedMessageIds: () -> Set<MessageId>?
let getSelectedMessageIds: () -> Set<EngineMessage.Id>?
init(openPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set<MessageId>?) {
init(openPeer: @escaping (EnginePeer, EnginePeer?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openMessage: @escaping (EnginePeer, EngineMessage.Id, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (EngineMessage.Id, Bool) -> Void, messageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set<EngineMessage.Id>?) {
self.openPeer = openPeer
self.openDisabledPeer = openDisabledPeer
self.openMessage = openMessage
@ -70,9 +69,9 @@ final class ChatListSearchInteraction {
}
private struct ChatListSearchContainerNodeSearchState: Equatable {
var selectedMessageIds: Set<MessageId>?
var selectedMessageIds: Set<EngineMessage.Id>?
func withUpdatedSelectedMessageIds(_ selectedMessageIds: Set<MessageId>?) -> ChatListSearchContainerNodeSearchState {
func withUpdatedSelectedMessageIds(_ selectedMessageIds: Set<EngineMessage.Id>?) -> ChatListSearchContainerNodeSearchState {
return ChatListSearchContainerNodeSearchState(selectedMessageIds: selectedMessageIds)
}
}
@ -80,10 +79,10 @@ private struct ChatListSearchContainerNodeSearchState: Equatable {
public final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
private let context: AccountContext
private let peersFilter: ChatListNodePeersFilter
private let groupId: PeerGroupId
private let groupId: EngineChatList.Group
private let displaySearchFilters: Bool
private var interaction: ChatListSearchInteraction?
private let openMessage: (Peer, MessageId, Bool) -> Void
private let openMessage: (EnginePeer, EngineMessage.Id, Bool) -> Void
private let navigationController: NavigationController?
let filterContainerNode: ChatListSearchFiltersContainerNode
@ -124,7 +123,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var validLayout: (ContainerViewLayout, CGFloat)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, groupId: EngineChatList.Group, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
self.context = context
self.peersFilter = filter
self.groupId = groupId
@ -224,7 +223,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
present(c, a)
}, dismissInput: { [weak self] in
self?.dismissInput()
}, getSelectedMessageIds: { [weak self] () -> Set<MessageId>? in
}, getSelectedMessageIds: { [weak self] () -> Set<EngineMessage.Id>? in
if let strongSelf = self {
return strongSelf.stateValue.selectedMessageIds
} else {
@ -304,11 +303,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.filterContainerNode.filterPressed?(initialFilter)
let suggestedPeers = self.searchQuery.get()
|> mapToSignal { query -> Signal<[Peer], NoError> in
|> mapToSignal { query -> Signal<[EnginePeer], NoError> in
if let query = query {
return context.account.postbox.searchPeers(query: query.lowercased())
|> map { local -> [Peer] in
return Array(local.compactMap { $0.peer }.prefix(10))
|> map { local -> [EnginePeer] in
return Array(local.compactMap { $0.peer }.prefix(10).map(EnginePeer.init))
}
} else {
return .single([])
@ -319,12 +318,12 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|> take(1)
self.suggestedFiltersDisposable.set((combineLatest(suggestedPeers, self.suggestedDates.get(), self.selectedFilterKeyPromise.get(), self.searchQuery.get(), accountPeer)
|> mapToSignal { peers, dates, selectedFilter, searchQuery, accountPeer -> Signal<([Peer], [(Date?, Date, String?)], ChatListSearchFilterEntryId?, String?, Peer?), NoError> in
|> mapToSignal { peers, dates, selectedFilter, searchQuery, accountPeer -> Signal<([EnginePeer], [(Date?, Date, String?)], ChatListSearchFilterEntryId?, String?, EnginePeer?), NoError> in
if searchQuery?.isEmpty ?? true {
return .single((peers, dates, selectedFilter, searchQuery, accountPeer))
return .single((peers, dates, selectedFilter, searchQuery, EnginePeer(accountPeer)))
} else {
return (.complete() |> delay(0.25, queue: Queue.mainQueue()))
|> then(.single((peers, dates, selectedFilter, searchQuery, accountPeer)))
|> then(.single((peers, dates, selectedFilter, searchQuery, EnginePeer(accountPeer))))
}
} |> map { peers, dates, selectedFilter, searchQuery, accountPeer -> [ChatListSearchFilter] in
var suggestedFilters: [ChatListSearchFilter] = []
@ -340,7 +339,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var existingPeerIds = Set<PeerId>()
var existingPeerIds = Set<EnginePeer.Id>()
var peers = peers
if let accountPeer = accountPeer, let lowercasedQuery = searchQuery?.lowercased(), lowercasedQuery.count > 1 && (presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery)) {
peers.insert(accountPeer, at: 0)
@ -354,7 +353,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let isGroup: Bool
if peer.id.namespace == Namespaces.Peer.SecretChat {
continue
} else if let channel = peer as? TelegramChannel, case .group = channel.info {
} else if case let .channel(channel) = peer, case .group = channel.info {
isGroup = true
} else if peer.id.namespace == Namespaces.Peer.CloudGroup {
isGroup = true
@ -362,8 +361,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
isGroup = false
}
var title: String = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
var compactDisplayTitle = EnginePeer(peer).compactDisplayTitle
var title: String = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
var compactDisplayTitle = peer.compactDisplayTitle
if peer.id == accountPeer?.id {
title = presentationData.strings.DialogList_SavedMessages
compactDisplayTitle = title
@ -566,11 +565,11 @@ 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 -> [Message] in
var messages: [Message] = []
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [EngineMessage] in
var messages: [EngineMessage] = []
for id in messageIds {
if let message = transaction.getMessage(id) {
messages.append(message)
messages.append(EngineMessage(message))
}
}
return messages
@ -579,7 +578,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
if let strongSelf = self, !messages.isEmpty {
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages.sorted(by: { lhs, rhs in
return lhs.index < rhs.index
})), externalShare: true, immediateExternalShare: true)
}).map({ $0._asMessage() })), externalShare: true, immediateExternalShare: true)
strongSelf.dismissInput()
strongSelf.present?(shareController, nil)
}
@ -641,12 +640,12 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.paneContainerNode.update(size: CGSize(width: layout.size.width, height: layout.size.height - topInset), sideInset: layout.safeInsets.left, bottomInset: bottomInset, visibleHeight: layout.size.height - topInset, presentationData: self.presentationData, availablePanes: availablePanes, transition: transition)
}
private var currentMessages: ([PeerId: Peer], [MessageId: Message]) {
var peers: [PeerId: Peer] = [:]
let messages: [MessageId: Message] = self.paneContainerNode.allCurrentMessages()
private var currentMessages: ([EnginePeer.Id: EnginePeer], [EngineMessage.Id: EngineMessage]) {
var peers: [EnginePeer.Id: EnginePeer] = [:]
let messages: [EngineMessage.Id: EngineMessage] = self.paneContainerNode.allCurrentMessages()
for (_, message) in messages {
for (_, peer) in message.peers {
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
}
}
return (peers, messages)
@ -665,11 +664,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let _ = self.paneContainerNode.scrollToTop()
}
private func messageContextAction(_ message: Message, node: ASDisplayNode?, rect: CGRect?, gesture anyRecognizer: UIGestureRecognizer?) {
private func messageContextAction(_ message: EngineMessage, node: ASDisplayNode?, rect: CGRect?, gesture anyRecognizer: UIGestureRecognizer?) {
guard let node = node as? ContextExtractedContentContainingNode else {
return
}
let _ = storedMessageFromSearch(account: self.context.account, message: message).start()
let _ = storedMessageFromSearch(account: self.context.account, message: message._asMessage()).start()
var linkForCopying: String?
var currentSupernode: ASDisplayNode? = node
@ -714,7 +713,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
})))
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
c.dismiss(completion: { [weak self] in
self?.openMessage(message.peers[message.id.peerId]!, message.id, false)
self?.openMessage(EnginePeer(message.peers[message.id.peerId]!), message.id, false)
})
})))
@ -741,9 +740,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.presentInGlobalOverlay?(controller, nil)
}
private func mediaMessageContextAction(_ message: Message, node: ASDisplayNode?, rect: CGRect?, gesture anyRecognizer: UIGestureRecognizer?) {
private func mediaMessageContextAction(_ message: EngineMessage, node: ASDisplayNode?, rect: CGRect?, gesture anyRecognizer: UIGestureRecognizer?) {
let gesture: ContextGesture? = anyRecognizer as? ContextGesture
let _ = (chatMediaListPreviewControllerData(context: self.context, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), message: message, standalone: true, reverseMessageGalleryOrder: false, navigationController: self.navigationController)
let _ = (chatMediaListPreviewControllerData(context: self.context, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), message: message._asMessage(), standalone: true, reverseMessageGalleryOrder: false, navigationController: self.navigationController)
|> deliverOnMainQueue).start(next: { [weak self] previewData in
guard let strongSelf = self else {
gesture?.cancel()
@ -760,7 +759,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { c, f in
c.dismiss(completion: {
self?.openMessage(message.peers[message.id.peerId]!, message.id, false)
self?.openMessage(EnginePeer(message.peers[message.id.peerId]!), message.id, false)
})
})))
@ -811,13 +810,13 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.setQuery?(nil, [], self.searchQueryValue ?? "")
}
func deleteMessages(messageIds: Set<MessageId>?) {
func deleteMessages(messageIds: Set<EngineMessage.Id>?) {
if let messageIds = messageIds ?? self.stateValue.selectedMessageIds, !messageIds.isEmpty {
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)
storeMessageFromSearch(transaction: transaction, message: message._asMessage())
}
}
}).start()
@ -878,14 +877,14 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
}
func forwardMessages(messageIds: Set<MessageId>?) {
func forwardMessages(messageIds: Set<EngineMessage.Id>?) {
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)
storeMessageFromSearch(transaction: transaction, message: message._asMessage())
}
}
}).start()
@ -902,7 +901,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let inputText = convertMarkdownToAttributes(messageText)
for text in breakChatInputText(trimChatInputText(inputText)) {
if text.length != 0 {
var attributes: [MessageAttribute] = []
var attributes: [EngineMessage.Attribute] = []
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
if !entities.isEmpty {
attributes.append(TextEntitiesMessageAttribute(entities: entities))
@ -912,14 +911,14 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
}
var attributes: [MessageAttribute] = []
var attributes: [EngineMessage.Attribute] = []
attributes.append(ForwardOptionsMessageAttribute(hideNames: forwardOptions?.hideNames == true, hideCaptions: forwardOptions?.hideCaptions == true))
result.append(contentsOf: messageIds.map { messageId -> EnqueueMessage in
return .forward(source: messageId, grouping: .auto, attributes: attributes, correlationId: nil)
})
var displayPeers: [Peer] = []
var displayPeers: [EnginePeer] = []
for peer in peers {
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result)
|> deliverOnMainQueue).start(next: { messageIds in
@ -947,10 +946,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
})
if let secretPeer = peer as? TelegramSecretChat {
if let peer = peerMap[secretPeer.regularPeerId] {
displayPeers.append(peer)
displayPeers.append(EnginePeer(peer))
}
} else {
displayPeers.append(peer)
displayPeers.append(EnginePeer(peer))
}
}
@ -962,14 +961,14 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
savedMessages = true
} else {
if displayPeers.count == 1, let peer = displayPeers.first {
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
} else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last {
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(firstPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(secondPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string
} else if let peer = displayPeers.first {
let peerName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string
} else {
text = ""

View File

@ -703,7 +703,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
if let entries = entries {
for entry in entries {
if case let .message(message, _, _, _, _, _, _) = entry {
self.mediaItems.append(VisualMediaItem(message: message, index: nil))
self.mediaItems.append(VisualMediaItem(message: message._asMessage(), index: nil))
}
index += 1
}

View File

@ -4,7 +4,6 @@ import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramPresentationData
import Postbox
import TelegramCore
import AccountContext
import ContextUI
@ -15,12 +14,12 @@ protocol ChatListSearchPaneNode: ASDisplayNode {
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
func scrollToTop() -> Bool
func cancelPreviewGestures()
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
func transitionNodeForGallery(messageId: EngineMessage.Id, media: EngineMedia) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
func addToTransitionSurface(view: UIView)
func updateHiddenMedia()
func updateSelectedMessages(animated: Bool)
func previewViewAndActionAtLocation(_ location: CGPoint) -> (UIView, CGRect, Any)?
var searchCurrentMessages: [Message]? { get }
var searchCurrentMessages: [EngineMessage]? { get }
}
final class ChatListSearchPaneWrapper {
@ -76,7 +75,7 @@ private final class ChatListSearchPendingPane {
interaction: ChatListSearchInteraction,
navigationController: NavigationController?,
peersFilter: ChatListNodePeersFilter,
groupId: PeerGroupId,
groupId: EngineChatList.Group,
searchQuery: Signal<String?, NoError>,
searchOptions: Signal<ChatListSearchOptions?, NoError>,
key: ChatListSearchPaneKey,
@ -102,7 +101,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD
private let context: AccountContext
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
private let peersFilter: ChatListNodePeersFilter
private let groupId: PeerGroupId
private let groupId: EngineChatList.Group
private let searchQuery: Signal<String?, NoError>
private let searchOptions: Signal<ChatListSearchOptions?, NoError>
private let navigationController: NavigationController?
@ -136,7 +135,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD
private var currentAvailablePanes: [ChatListSearchPaneKey]?
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peersFilter: ChatListNodePeersFilter, groupId: PeerGroupId, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peersFilter: ChatListNodePeersFilter, groupId: EngineChatList.Group, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.peersFilter = peersFilter
@ -280,11 +279,11 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD
self.currentPane?.node.updateHiddenMedia()
}
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
func transitionNodeForGallery(messageId: EngineMessage.Id, media: EngineMedia) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
return self.currentPane?.node.transitionNodeForGallery(messageId: messageId, media: media)
}
func updateSelectedMessageIds(_ selectedMessageIds: Set<MessageId>?, animated: Bool) {
func updateSelectedMessageIds(_ selectedMessageIds: Set<EngineMessage.Id>?, animated: Bool) {
for (_, pane) in self.currentPanes {
pane.node.updateSelectedMessages(animated: animated)
}
@ -497,8 +496,8 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerD
self.currentPaneUpdated?(self.currentPaneKey, self.transitionFraction, transition)
}
func allCurrentMessages() -> [MessageId: Message] {
var allMessages: [MessageId: Message] = [:]
func allCurrentMessages() -> [EngineMessage.Id: EngineMessage] {
var allMessages: [EngineMessage.Id: EngineMessage] = [:]
for (_, pane) in self.currentPanes {
if let messages = pane.node.searchCurrentMessages {
for message in messages {

View File

@ -1,7 +1,6 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Postbox
import Display
import SwiftSignalKit
import TelegramCore
@ -20,8 +19,23 @@ import ContextUI
import ChatInterfaceState
public enum ChatListItemContent {
case peer(messages: [Message], peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: StoredPeerChatInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool)
case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool)
public final class DraftState: Equatable {
let text: String
public init(text: String) {
self.text = text
}
public static func ==(lhs: DraftState, rhs: DraftState) -> Bool {
if lhs.text != rhs.text {
return false
}
return true
}
}
case peer(messages: [EngineMessage], peer: EngineRenderedPeer, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool)
case groupReference(groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool)
public var chatLocation: ChatLocation? {
switch self {
@ -36,9 +50,9 @@ public enum ChatListItemContent {
public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
let presentationData: ChatListPresentationData
let context: AccountContext
let peerGroupId: PeerGroupId
let peerGroupId: EngineChatList.Group
let filterData: ChatListItemFilterData?
let index: ChatListIndex
let index: EngineChatList.Item.Index
public let content: ChatListItemContent
let editing: Bool
let hasActiveRevealControls: Bool
@ -59,7 +73,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
return self.index.pinningIndex != nil
}
public init(presentationData: ChatListPresentationData, context: AccountContext, peerGroupId: PeerGroupId, filterData: ChatListItemFilterData?, index: ChatListIndex, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, header: ListViewItemHeader?, enableContextActions: Bool, hiddenOffset: Bool, interaction: ChatListNodeInteraction) {
public init(presentationData: ChatListPresentationData, context: AccountContext, peerGroupId: EngineChatList.Group, filterData: ChatListItemFilterData?, index: EngineChatList.Item.Index, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, header: ListViewItemHeader?, enableContextActions: Bool, hiddenOffset: Bool, interaction: ChatListNodeInteraction) {
self.presentationData = presentationData
self.peerGroupId = peerGroupId
self.filterData = filterData
@ -195,7 +209,7 @@ private enum RevealOptionKey: Int32 {
case hidePsa
}
private func canArchivePeer(id: PeerId, accountPeerId: PeerId) -> Bool {
private func canArchivePeer(id: EnginePeer.Id, accountPeerId: EnginePeer.Id) -> Bool {
if id.namespace == Namespaces.Peer.CloudUser && id.id._internalGetInt64Value() == 777000 {
return false
}
@ -213,10 +227,10 @@ public struct ChatListItemFilterData: Equatable {
}
}
private func revealOptions(strings: PresentationStrings, theme: PresentationTheme, isPinned: Bool, isMuted: Bool?, groupId: PeerGroupId, peerId: PeerId, accountPeerId: PeerId, canDelete: Bool, isEditing: Bool, filterData: ChatListItemFilterData?) -> [ItemListRevealOption] {
private func revealOptions(strings: PresentationStrings, theme: PresentationTheme, isPinned: Bool, isMuted: Bool?, groupId: EngineChatList.Group, peerId: EnginePeer.Id, accountPeerId: EnginePeer.Id, canDelete: Bool, isEditing: Bool, filterData: ChatListItemFilterData?) -> [ItemListRevealOption] {
var options: [ItemListRevealOption] = []
if !isEditing {
if case .group = groupId {
if case .archive = groupId {
if isPinned {
options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor))
} else {
@ -272,8 +286,8 @@ private func groupReferenceRevealOptions(strings: PresentationStrings, theme: Pr
return options
}
private func leftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool, isSavedMessages: Bool, groupId: PeerGroupId, peer: Peer, filterData: ChatListItemFilterData?) -> [ItemListRevealOption] {
if case .group = groupId {
private func leftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool, isSavedMessages: Bool, groupId: EngineChatList.Group, peer: EnginePeer, filterData: ChatListItemFilterData?) -> [ItemListRevealOption] {
if case .archive = groupId {
return []
}
var options: [ItemListRevealOption] = []
@ -332,8 +346,8 @@ private let playIconImage = UIImage(bundleImageName: "Chat List/MiniThumbnailPla
private final class ChatListMediaPreviewNode: ASDisplayNode {
private let context: AccountContext
private let message: Message
private let media: Media
private let message: EngineMessage
private let media: EngineMedia
private let imageNode: TransformImageNode
private let playIcon: ASImageNode
@ -341,7 +355,7 @@ private final class ChatListMediaPreviewNode: ASDisplayNode {
private var requestedImage: Bool = false
private var disposable: Disposable?
init(context: AccountContext, message: Message, media: Media) {
init(context: AccountContext, message: EngineMessage, media: EngineMedia) {
self.context = context
self.message = message
self.media = media
@ -366,17 +380,17 @@ private final class ChatListMediaPreviewNode: ASDisplayNode {
}
var dimensions = CGSize(width: 100.0, height: 100.0)
if let image = self.media as? TelegramMediaImage {
if case let .image(image) = self.media {
self.playIcon.isHidden = true
if let largest = largestImageRepresentation(image.representations) {
dimensions = largest.dimensions.cgSize
if !self.requestedImage {
self.requestedImage = true
let signal = mediaGridMessagePhoto(account: self.context.account, photoReference: .message(message: MessageReference(self.message), media: image), fullRepresentationSize: CGSize(width: 36.0, height: 36.0), synchronousLoad: synchronousLoads)
let signal = mediaGridMessagePhoto(account: self.context.account, photoReference: .message(message: MessageReference(self.message._asMessage()), media: image), fullRepresentationSize: CGSize(width: 36.0, height: 36.0), synchronousLoad: synchronousLoads)
self.imageNode.setSignal(signal, attemptSynchronously: synchronousLoads)
}
}
} else if let file = self.media as? TelegramMediaFile {
} else if case let .file(file) = self.media {
if file.isAnimated {
self.playIcon.isHidden = true
} else {
@ -386,7 +400,7 @@ private final class ChatListMediaPreviewNode: ASDisplayNode {
dimensions = mediaDimensions.cgSize
if !self.requestedImage {
self.requestedImage = true
let signal = mediaGridMessageVideo(postbox: self.context.account.postbox, videoReference: .message(message: MessageReference(self.message), media: file), synchronousLoad: synchronousLoads, autoFetchFullSizeThumbnail: true, useMiniThumbnailIfAvailable: true)
let signal = mediaGridMessageVideo(postbox: self.context.account.postbox, videoReference: .message(message: MessageReference(self.message._asMessage()), media: file), synchronousLoad: synchronousLoads, autoFetchFullSizeThumbnail: true, useMiniThumbnailIfAvailable: true)
self.imageNode.setSignal(signal, attemptSynchronously: synchronousLoads)
}
}
@ -426,8 +440,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let mutedIconNode: ASImageNode
private var currentTextLeftCutout: CGFloat = 0.0
private var currentMediaPreviewSpecs: [(message: Message, media: Media, size: CGSize)] = []
private var mediaPreviewNodes: [MediaId: ChatListMediaPreviewNode] = [:]
private var currentMediaPreviewSpecs: [(message: EngineMessage, media: EngineMedia, size: CGSize)] = []
private var mediaPreviewNodes: [EngineMedia.Id: ChatListMediaPreviewNode] = [:]
var selectableControlNode: ItemListSelectableControlNode?
var reorderControlNode: ItemListEditableReorderControlNode?
@ -477,11 +491,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
return nil
}
switch item.content {
case let .groupReference(_, _, _, unreadState, _):
case let .groupReference(_, _, _, unreadCount, _):
var result = item.presentationData.strings.ChatList_ArchivedChatsTitle
let allCount = unreadState.count(countingCategory: .chats, mutedCategory: .all)
let allCount = unreadCount
if allCount > 0 {
result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(allCount))"
result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(Int32(allCount)))"
}
return result
case let .peer(_, peer, combinedReadState, _, _, _, _, _, _, _, _, _):
@ -492,7 +506,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if item.context.account.peerId == chatMainPeer.id {
result += item.presentationData.strings.DialogList_SavedMessages
} else {
result += EnginePeer(chatMainPeer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
result += chatMainPeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
}
if let combinedReadState = combinedReadState, combinedReadState.count > 0 {
result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(combinedReadState.count))"
@ -519,8 +533,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
}
let (_, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, author is TelegramUser {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(EnginePeer(author).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
}
result += "\n\(messageText)"
return result
@ -529,7 +543,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var isFirst = true
for peer in peers {
if let chatMainPeer = peer.peer.chatMainPeer {
let peerTitle = EnginePeer(chatMainPeer).compactDisplayTitle
let peerTitle = chatMainPeer.compactDisplayTitle
if !peerTitle.isEmpty {
if isFirst {
isFirst = false
@ -553,8 +567,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
}
let (_, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, author is TelegramUser {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(EnginePeer(author).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
}
if !message.flags.contains(.Incoming), let combinedReadState = combinedReadState, combinedReadState.isOutgoingMessageIndexRead(message.index) {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageRead)"
@ -662,14 +676,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let previousItem = self.item
self.item = item
var peer: Peer?
var peer: EnginePeer?
var displayAsMessage = false
var enablePreview = true
switch item.content {
case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, displayAsMessageValue, _):
displayAsMessage = displayAsMessageValue
if displayAsMessage, let author = messages.last?.author as? TelegramUser {
peer = author
if displayAsMessage, case let .user(author) = messages.last?.author {
peer = .user(author)
} else {
peer = peerValue.chatMainPeer
}
@ -681,7 +695,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
UIView.transition(with: self.avatarNode.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
}, completion: nil)
}
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer.flatMap(EnginePeer.init), overrideImage: .archivedChatsIcon(hiddenByDefault: hiddenByDefault), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: .archivedChatsIcon(hiddenByDefault: hiddenByDefault), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
}
if let peer = peer {
@ -693,7 +707,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if peer.isDeleted {
overrideImage = .deletedIcon
}
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(peer), overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0))
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0))
}
self.contextContainer.isGestureEnabled = enablePreview && !item.editing
@ -804,19 +818,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let badgeFont = Font.with(size: floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0), design: .regular, weight: .regular, traits: [.monospacedNumbers])
let account = item.context.account
var messages: [Message]
var messages: [EngineMessage]
enum ContentPeer {
case chat(RenderedPeer)
case group([ChatListGroupReferencePeer])
case chat(EngineRenderedPeer)
case group([EngineChatList.GroupItem.Item])
}
let contentPeer: ContentPeer
let combinedReadState: CombinedPeerReadState?
let combinedReadState: EnginePeerReadCounters?
let unreadCount: (count: Int32, unread: Bool, muted: Bool, mutedCount: Int32?)
let isRemovedFromTotalUnreadCount: Bool
let peerPresence: PeerPresence?
let embeddedState: StoredPeerChatInterfaceState?
let summaryInfo: ChatListMessageTagSummaryInfo
let inputActivities: [(Peer, PeerInputActivity)]?
let peerPresence: EnginePeer.Presence?
let draftState: ChatListItemContent.DraftState?
let hasUnseenMentions: Bool
let inputActivities: [(EnginePeer, PeerInputActivity)]?
let isPeerGroup: Bool
let promoInfo: ChatListNodeEntryPromoInfo?
let displayAsMessage: Bool
@ -825,7 +839,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var groupHiddenByDefault = false
switch item.content {
case let .peer(messagesValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _):
case let .peer(messagesValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, hasUnseenMentionsValue, draftStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _):
messages = messagesValue
contentPeer = .chat(peerValue)
combinedReadState = combinedReadStateValue
@ -839,15 +853,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
isRemovedFromTotalUnreadCount = isRemovedFromTotalUnreadCountValue
}
peerPresence = (peerPresenceValue as? TelegramUserPresence).flatMap { presence -> TelegramUserPresence in
TelegramUserPresence(status: presence.status, lastActivity: 0)
peerPresence = peerPresenceValue.flatMap { presence -> EnginePeer.Presence in
return EnginePeer.Presence(status: presence.status, lastActivity: 0)
}
embeddedState = embeddedStateValue
summaryInfo = summaryInfoValue
draftState = draftStateValue
hasUnseenMentions = hasUnseenMentionsValue
switch peerValue.peer {
case _ as TelegramUser, _ as TelegramSecretChat:
if let peerPresence = peerPresence as? TelegramUserPresence, case .present = peerPresence.status {
case .user, .secretChat:
if let peerPresence = peerPresence, case .present = peerPresence.status {
inputActivities = inputActivitiesValue
} else {
inputActivities = nil
@ -860,7 +874,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
promoInfo = promoInfoValue
displayAsMessage = displayAsMessageValue
hasFailedMessages = messagesValue.last?.flags.contains(.Failed) ?? false // hasFailedMessagesValue
case let .groupReference(_, peers, messageValue, unreadState, hiddenByDefault):
case let .groupReference(_, peers, messageValue, unreadCountValue, hiddenByDefault):
if let _ = messageValue, !peers.isEmpty {
contentPeer = .chat(peers[0].peer)
} else {
@ -873,13 +887,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
combinedReadState = nil
isRemovedFromTotalUnreadCount = false
embeddedState = nil
summaryInfo = ChatListMessageTagSummaryInfo()
draftState = nil
hasUnseenMentions = false
inputActivities = nil
isPeerGroup = true
groupHiddenByDefault = hiddenByDefault
let allCount = unreadState.count(countingCategory: .chats, mutedCategory: .all)
unreadCount = (allCount, allCount != 0, true, nil)
unreadCount = (Int32(unreadCountValue), unreadCountValue != 0, true, nil)
peerPresence = nil
promoInfo = nil
displayAsMessage = false
@ -949,8 +962,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let leftInset: CGFloat = params.leftInset + avatarLeftInset
enum ContentData {
case chat(itemPeer: RenderedPeer, peer: Peer?, hideAuthor: Bool, messageText: String)
case group(peers: [ChatListGroupReferencePeer])
case chat(itemPeer: EngineRenderedPeer, peer: EnginePeer?, hideAuthor: Bool, messageText: String)
case group(peers: [EngineChatList.GroupItem.Item])
}
let contentData: ContentData
@ -977,11 +990,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var inlineAuthorPrefix: String?
if case .groupReference = item.content {
if let author = messages.last?.author as? TelegramUser {
if case let .user(author) = messages.last?.author {
if author.id == item.context.account.peerId {
inlineAuthorPrefix = item.presentationData.strings.DialogList_You
} else if messages.last?.id.peerId.namespace != Namespaces.Peer.CloudUser && messages.last?.id.peerId.namespace != Namespaces.Peer.SecretChat {
inlineAuthorPrefix = EnginePeer(author).compactDisplayTitle
inlineAuthorPrefix = EnginePeer.user(author).compactDisplayTitle
}
}
}
@ -993,17 +1006,22 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let contentImageSize = CGSize(width: contentImageSide, height: contentImageSide)
let contentImageSpacing: CGFloat = 2.0
let contentImageTrailingSpace: CGFloat = 5.0
var contentImageSpecs: [(message: Message, media: Media, size: CGSize)] = []
var contentImageSpecs: [(message: EngineMessage, media: EngineMedia, size: CGSize)] = []
switch contentData {
case let .chat(itemPeer, _, _, text):
var isUser = false
if case .user = itemPeer.chatMainPeer {
isUser = true
}
var peerText: String?
if case .groupReference = item.content {
if let messagePeer = itemPeer.chatMainPeer {
peerText = EnginePeer(messagePeer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
}
} else if let message = messages.last, let author = message.author as? TelegramUser, let peer = itemPeer.chatMainPeer, !(peer is TelegramUser) {
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
} else if let message = messages.last, case let .user(author) = message.author, let peer = itemPeer.chatMainPeer, !isUser {
if case let .channel(peer) = peer, case .broadcast = peer.info {
} else if !displayAsMessage {
if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported), let authorSignature = forwardInfo.authorSignature {
peerText = authorSignature
@ -1022,13 +1040,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
chatListText = (text, messageText)
}
if inlineAuthorPrefix == nil, let embeddedState = embeddedState, embeddedState.overrideChatTimestamp != nil, let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
let interfaceState = ChatInterfaceState.parse(opaqueState)
if inlineAuthorPrefix == nil, let draftState = draftState {
hasDraft = true
authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor)
let draftText: String = interfaceState.composeInputState.inputText.string
let draftText: String = draftState.text
attributedText = NSAttributedString(string: foldLineBreaks(draftText.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor)
} else if let message = messages.last {
@ -1081,7 +1097,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
var displayMediaPreviews = true
if message.containsSecretMedia {
if message._asMessage().containsSecretMedia {
displayMediaPreviews = false
} else if let _ = message.peers[message.id.peerId] as? TelegramSecretChat {
displayMediaPreviews = false
@ -1096,34 +1112,28 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
inner: for media in message.media {
if let image = media as? TelegramMediaImage {
if let _ = largestImageRepresentation(image.representations) {
//let imageSize = largest.dimensions.cgSize
//let fitSize = imageSize.aspectFilled(contentImageFillSize)
let fitSize = contentImageSize
contentImageSpecs.append((message, image, fitSize))
contentImageSpecs.append((message, .image(image), fitSize))
}
break inner
} else if let file = media as? TelegramMediaFile {
if file.isVideo, !file.isInstantVideo, let _ = file.dimensions {
//let imageSize = dimensions.cgSize
//let fitSize = imageSize.aspectFilled(contentImageFillSize)
let fitSize = contentImageSize
contentImageSpecs.append((message, file, fitSize))
contentImageSpecs.append((message, .file(file), fitSize))
}
break inner
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
let imageTypes = ["photo", "video", "embed", "gif", "document", "telegram_album"]
if let image = content.image, let type = content.type, imageTypes.contains(type) {
if let _ = largestImageRepresentation(image.representations) {
//let imageSize = largest.dimensions.cgSize
let fitSize = contentImageSize
contentImageSpecs.append((message, image, fitSize))
contentImageSpecs.append((message, .image(image), fitSize))
}
break inner
} else if let file = content.file {
if file.isVideo, !file.isInstantVideo, let _ = file.dimensions {
//let imageSize = dimensions.cgSize
let fitSize = contentImageSize
contentImageSpecs.append((message, file, fitSize))
contentImageSpecs.append((message, .file(file), fitSize))
}
break inner
}
@ -1137,7 +1147,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var peerText: String?
if case .groupReference = item.content {
if let messagePeer = itemPeer.chatMainPeer {
peerText = EnginePeer(messagePeer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
}
}
@ -1150,7 +1160,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var isFirst = true
for peer in peers {
if let chatMainPeer = peer.peer.chatMainPeer {
let peerTitle = EnginePeer(chatMainPeer).compactDisplayTitle
let peerTitle = chatMainPeer.compactDisplayTitle
if !peerTitle.isEmpty {
if isFirst {
isFirst = false
@ -1176,15 +1186,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
switch contentData {
case let .chat(itemPeer, _, _, _):
if let message = messages.last, let author = message.author as? TelegramUser, displayAsMessage {
titleAttributedString = NSAttributedString(string: author.id == account.peerId ? item.presentationData.strings.DialogList_You : EnginePeer(author).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder), font: titleFont, textColor: theme.titleColor)
if let message = messages.last, case let .user(author) = message.author, displayAsMessage {
titleAttributedString = NSAttributedString(string: author.id == account.peerId ? item.presentationData.strings.DialogList_You : EnginePeer.user(author).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder), font: titleFont, textColor: theme.titleColor)
} else if isPeerGroup {
titleAttributedString = NSAttributedString(string: item.presentationData.strings.ChatList_ArchivedChatsTitle, font: titleFont, textColor: theme.titleColor)
} else if itemPeer.chatMainPeer?.id == item.context.account.peerId {
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor)
} else if let id = itemPeer.chatMainPeer?.id, id.isReplies {
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: titleFont, textColor: theme.titleColor)
} else if let displayTitle = itemPeer.chatMainPeer.flatMap(EnginePeer.init)?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) {
} else if let displayTitle = itemPeer.chatMainPeer?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) {
titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: item.index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat ? theme.secretTitleColor : theme.titleColor)
}
case .group:
@ -1221,7 +1231,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
if !isPeerGroup, let message = messages.last, message.author?.id == account.peerId && !hasDraft {
if message.flags.isSending && !message.isSentOrAcknowledged {
if message.flags.isSending && !message._asMessage().isSentOrAcknowledged {
statusState = .clock(PresentationResourcesChatList.clockFrameImage(item.presentationData.theme), PresentationResourcesChatList.clockMinImage(item.presentationData.theme))
} else if message.id.peerId != account.peerId {
if hasFailedMessages {
@ -1264,12 +1274,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
let tagSummaryCount = summaryInfo.tagSummaryCount ?? 0
let actionsSummaryCount = summaryInfo.actionsSummaryCount ?? 0
let totalMentionCount = tagSummaryCount - actionsSummaryCount
if !isPeerGroup {
if totalMentionCount > 0 {
if Namespaces.PeerGroup.archive == item.peerGroupId {
if hasUnseenMentions {
if case .archive = item.peerGroupId {
currentMentionBadgeImage = PresentationResourcesChatList.badgeBackgroundInactiveMention(item.presentationData.theme, diameter: badgeDiameter)
} else {
currentMentionBadgeImage = PresentationResourcesChatList.badgeBackgroundMention(item.presentationData.theme, diameter: badgeDiameter)
@ -1414,20 +1421,20 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
switch item.content {
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage, _):
if !displayAsMessage {
if let peer = renderedPeer.chatMainPeer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
let updatedPresence = TelegramUserPresence(status: presence.status, lastActivity: 0)
let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(updatedPresence), relativeTo: timestamp)
if case let .user(peer) = renderedPeer.chatMainPeer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
let updatedPresence = EnginePeer.Presence(status: presence.status, lastActivity: 0)
let relativeStatus = relativeUserPresenceStatus(updatedPresence, relativeTo: timestamp)
if case .online = relativeStatus {
online = true
}
animateOnline = true
} else if let channel = renderedPeer.peer as? TelegramChannel {
} else if case let .channel(channel) = renderedPeer.peer {
onlineIsVoiceChat = true
if channel.flags.contains(.hasActiveVoiceChat) && item.interaction.searchTextHighightState == nil {
online = true
}
animateOnline = true
} else if let group = renderedPeer.peer as? TelegramGroup {
} else if case let .legacyGroup(group) = renderedPeer.peer {
onlineIsVoiceChat = true
if group.flags.contains(.hasActiveVoiceChat) && item.interaction.searchTextHighightState == nil {
online = true
@ -1780,7 +1787,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
inputActivitiesApply?()
var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: floor((measureLayout.size.height - contentImageSize.height) / 2.0))
var validMediaIds: [MediaId] = []
var validMediaIds: [EngineMedia.Id] = []
for (message, media, mediaSize) in contentImageSpecs {
guard let mediaId = media.id else {
continue
@ -1803,7 +1810,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
previewNodeTransition.updateFrame(node: previewNode, frame: CGRect(origin: mediaPreviewOffset, size: mediaSize))
mediaPreviewOffset.x += mediaSize.width + contentImageSpacing
}
var removeMediaIds: [MediaId] = []
var removeMediaIds: [EngineMedia.Id] = []
for (mediaId, itemNode) in strongSelf.mediaPreviewNodes {
if !validMediaIds.contains(mediaId) {
removeMediaIds.append(mediaId)
@ -1864,8 +1871,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let topNegativeInset: CGFloat = 0.0
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layoutOffset - separatorHeight - topNegativeInset), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height + separatorHeight + topNegativeInset))
if let peerPresence = peerPresence as? TelegramUserPresence {
strongSelf.peerPresenceManager?.reset(presence: EnginePeer.Presence(TelegramUserPresence(status: peerPresence.status, lastActivity: 0)), isOnline: online)
if let peerPresence = peerPresence {
strongSelf.peerPresenceManager?.reset(presence: EnginePeer.Presence(status: peerPresence.status, lastActivity: 0), isOnline: online)
}
strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
@ -2057,7 +2064,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
case RevealOptionKey.pin.rawValue:
switch item.content {
case .peer:
let itemId: PinnedItemId = .peer(item.index.messageIndex.id.peerId)
let itemId: EngineChatList.PinnedItem.Id = .peer(item.index.messageIndex.id.peerId)
item.interaction.setItemPinned(itemId, true)
case .groupReference:
break
@ -2065,7 +2072,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
case RevealOptionKey.unpin.rawValue:
switch item.content {
case .peer:
let itemId: PinnedItemId = .peer(item.index.messageIndex.id.peerId)
let itemId: EngineChatList.PinnedItem.Id = .peer(item.index.messageIndex.id.peerId)
item.interaction.setItemPinned(itemId, false)
case .groupReference:
break

View File

@ -1,5 +1,4 @@
import Foundation
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
@ -14,7 +13,7 @@ private enum MessageGroupType {
case generic
}
private func singleMessageType(message: Message) -> MessageGroupType {
private func singleMessageType(message: EngineMessage) -> MessageGroupType {
for media in message.media {
if let _ = media as? TelegramMediaImage {
return .photos
@ -31,7 +30,7 @@ private func singleMessageType(message: Message) -> MessageGroupType {
return .generic
}
private func messageGroupType(messages: [Message]) -> MessageGroupType {
private func messageGroupType(messages: [EngineMessage]) -> MessageGroupType {
if messages.isEmpty {
return .generic
}
@ -45,16 +44,16 @@ private func messageGroupType(messages: [Message]) -> MessageGroupType {
return currentType
}
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, messages: [Message], chatPeer: RenderedPeer, accountPeerId: PeerId, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: Peer?, hideAuthor: Bool, messageText: String) {
let peer: Peer?
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String) {
let peer: EnginePeer?
let message = messages.last
var hideAuthor = false
var messageText: String
if let message = message {
if let messageMain = messageMainPeer(EngineMessage(message)) {
peer = messageMain._asPeer()
if let messageMain = messageMainPeer(message) {
peer = messageMain
} else {
peer = chatPeer.chatMainPeer
}
@ -261,12 +260,12 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
}
default:
hideAuthor = true
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), accountPeerId: accountPeerId, forChatList: true) {
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: true) {
messageText = text
}
}
case _ as TelegramMediaExpiredContent:
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), accountPeerId: accountPeerId, forChatList: true) {
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: true) {
messageText = text
}
case let poll as TelegramMediaPoll:
@ -282,21 +281,21 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
peer = chatPeer.chatMainPeer
messageText = ""
if chatPeer.peerId.namespace == Namespaces.Peer.SecretChat {
if let secretChat = chatPeer.peers[chatPeer.peerId] as? TelegramSecretChat {
if case let .secretChat(secretChat) = chatPeer.peers[chatPeer.peerId] {
switch secretChat.embeddedState {
case .active:
switch secretChat.role {
case .creator:
messageText = strings.DialogList_EncryptedChatStartedOutgoing(peer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? "").string
messageText = strings.DialogList_EncryptedChatStartedOutgoing(peer?.compactDisplayTitle ?? "").string
case .participant:
messageText = strings.DialogList_EncryptedChatStartedIncoming(peer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? "").string
messageText = strings.DialogList_EncryptedChatStartedIncoming(peer?.compactDisplayTitle ?? "").string
}
case .terminated:
messageText = strings.DialogList_EncryptionRejected
case .handshake:
switch secretChat.role {
case .creator:
messageText = strings.DialogList_AwaitingEncryption(peer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? "").string
messageText = strings.DialogList_AwaitingEncryption(peer?.compactDisplayTitle ?? "").string
case .participant:
messageText = strings.DialogList_EncryptionProcessing
}

View File

@ -4,7 +4,6 @@ import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramCore
import Postbox
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
@ -48,34 +47,34 @@ final class ChatListHighlightedLocation {
public final class ChatListNodeInteraction {
public enum PeerEntry {
case peerId(PeerId)
case peer(Peer)
case peerId(EnginePeer.Id)
case peer(EnginePeer)
}
let activateSearch: () -> Void
let peerSelected: (Peer, Peer?, ChatListNodeEntryPromoInfo?) -> Void
let disabledPeerSelected: (Peer) -> Void
let togglePeerSelected: (Peer) -> Void
let peerSelected: (EnginePeer, EnginePeer?, ChatListNodeEntryPromoInfo?) -> Void
let disabledPeerSelected: (EnginePeer) -> Void
let togglePeerSelected: (EnginePeer) -> Void
let togglePeersSelection: ([PeerEntry], Bool) -> Void
let additionalCategorySelected: (Int) -> Void
let messageSelected: (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void
let groupSelected: (PeerGroupId) -> Void
let messageSelected: (EnginePeer, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void
let groupSelected: (EngineChatList.Group) -> Void
let addContact: (String) -> Void
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
let setItemPinned: (PinnedItemId, Bool) -> Void
let setPeerMuted: (PeerId, Bool) -> Void
let deletePeer: (PeerId, Bool) -> Void
let updatePeerGrouping: (PeerId, Bool) -> Void
let togglePeerMarkedUnread: (PeerId, Bool) -> Void
let setPeerIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void
let setItemPinned: (EngineChatList.PinnedItem.Id, Bool) -> Void
let setPeerMuted: (EnginePeer.Id, Bool) -> Void
let deletePeer: (EnginePeer.Id, Bool) -> Void
let updatePeerGrouping: (EnginePeer.Id, Bool) -> Void
let togglePeerMarkedUnread: (EnginePeer.Id, Bool) -> Void
let toggleArchivedFolderHiddenByDefault: () -> Void
let hidePsa: (PeerId) -> Void
let hidePsa: (EnginePeer.Id) -> Void
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?) -> Void
let present: (ViewController) -> Void
public var searchTextHighightState: String?
var highlightedChatLocation: ChatListHighlightedLocation?
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, Peer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (Peer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId, Bool) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (EnginePeer, EnginePeer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, togglePeerSelected: @escaping (EnginePeer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (EnginePeer, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (EngineChatList.Group) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, setItemPinned: @escaping (EngineChatList.PinnedItem.Id, Bool) -> Void, setPeerMuted: @escaping (EnginePeer.Id, Bool) -> Void, deletePeer: @escaping (EnginePeer.Id, Bool) -> Void, updatePeerGrouping: @escaping (EnginePeer.Id, Bool) -> Void, togglePeerMarkedUnread: @escaping (EnginePeer.Id, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (EnginePeer.Id) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
self.activateSearch = activateSearch
self.peerSelected = peerSelected
self.disabledPeerSelected = disabledPeerSelected
@ -99,19 +98,19 @@ public final class ChatListNodeInteraction {
}
public final class ChatListNodePeerInputActivities {
public let activities: [PeerId: [(Peer, PeerInputActivity)]]
public let activities: [EnginePeer.Id: [(EnginePeer, PeerInputActivity)]]
public init(activities: [PeerId: [(Peer, PeerInputActivity)]]) {
public init(activities: [EnginePeer.Id: [(EnginePeer, PeerInputActivity)]]) {
self.activities = activities
}
}
private func areFoundPeerArraysEqual(_ lhs: [(Peer, Peer?)], _ rhs: [(Peer, Peer?)]) -> Bool {
private func areFoundPeerArraysEqual(_ lhs: [(EnginePeer, EnginePeer?)], _ rhs: [(EnginePeer, EnginePeer?)]) -> Bool {
if lhs.count != rhs.count {
return false
}
for i in 0 ..< lhs.count {
if !arePeersEqual(lhs[i].0, rhs[i].0) || !arePeersEqual(lhs[i].1, rhs[i].1) {
if lhs[i].0 != rhs[i].0 || lhs[i].1 != rhs[i].1 {
return false
}
}
@ -121,18 +120,18 @@ private func areFoundPeerArraysEqual(_ lhs: [(Peer, Peer?)], _ rhs: [(Peer, Peer
public struct ChatListNodeState: Equatable {
public var presentationData: ChatListPresentationData
public var editing: Bool
public var peerIdWithRevealedOptions: PeerId?
public var selectedPeerIds: Set<PeerId>
public var peerIdWithRevealedOptions: EnginePeer.Id?
public var selectedPeerIds: Set<EnginePeer.Id>
public var peerInputActivities: ChatListNodePeerInputActivities?
public var pendingRemovalPeerIds: Set<PeerId>
public var pendingClearHistoryPeerIds: Set<PeerId>
public var pendingRemovalPeerIds: Set<EnginePeer.Id>
public var pendingClearHistoryPeerIds: Set<EnginePeer.Id>
public var archiveShouldBeTemporaryRevealed: Bool
public var selectedAdditionalCategoryIds: Set<Int>
public var hiddenPsaPeerId: PeerId?
public var foundPeers: [(Peer, Peer?)]
public var selectedPeerMap: [PeerId: Peer]
public var hiddenPsaPeerId: EnginePeer.Id?
public var foundPeers: [(EnginePeer, EnginePeer?)]
public var selectedPeerMap: [EnginePeer.Id: EnginePeer]
public init(presentationData: ChatListPresentationData, editing: Bool, peerIdWithRevealedOptions: PeerId?, selectedPeerIds: Set<PeerId>, foundPeers: [(Peer, Peer?)], selectedPeerMap: [PeerId: Peer], selectedAdditionalCategoryIds: Set<Int>, peerInputActivities: ChatListNodePeerInputActivities?, pendingRemovalPeerIds: Set<PeerId>, pendingClearHistoryPeerIds: Set<PeerId>, archiveShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: PeerId?) {
public init(presentationData: ChatListPresentationData, editing: Bool, peerIdWithRevealedOptions: EnginePeer.Id?, selectedPeerIds: Set<EnginePeer.Id>, foundPeers: [(EnginePeer, EnginePeer?)], selectedPeerMap: [EnginePeer.Id: EnginePeer], selectedAdditionalCategoryIds: Set<Int>, peerInputActivities: ChatListNodePeerInputActivities?, pendingRemovalPeerIds: Set<EnginePeer.Id>, pendingClearHistoryPeerIds: Set<EnginePeer.Id>, archiveShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: EnginePeer.Id?) {
self.presentationData = presentationData
self.editing = editing
self.peerIdWithRevealedOptions = peerIdWithRevealedOptions
@ -163,7 +162,7 @@ public struct ChatListNodeState: Equatable {
if areFoundPeerArraysEqual(lhs.foundPeers, rhs.foundPeers) {
return false
}
if arePeerDictionariesEqual(lhs.selectedPeerMap, rhs.selectedPeerMap) {
if lhs.selectedPeerMap != rhs.selectedPeerMap {
return false
}
if lhs.selectedAdditionalCategoryIds != rhs.selectedAdditionalCategoryIds {
@ -188,7 +187,7 @@ public struct ChatListNodeState: Equatable {
}
}
private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionInsertEntry]) -> [ListViewInsertItem] {
private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: EngineChatList.Group, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionInsertEntry]) -> [ListViewInsertItem] {
return entries.map { entry -> ListViewInsertItem in
switch entry.entry {
case .HeaderEntry:
@ -211,20 +210,47 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
nodeInteraction.additionalCategorySelected(id)
}
), directionHint: entry.directionHint)
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, presence, hasUnseenMentions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
switch mode {
case .chatList:
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .peer(messages: messages, peer: peer, combinedReadState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: hasFailedMessages), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: presentationData,
context: context,
peerGroupId: peerGroupId,
filterData: filterData,
index: index,
content: .peer(
messages: messages,
peer: peer,
combinedReadState: combinedReadState,
isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount,
presence: presence,
hasUnseenMentions: hasUnseenMentions,
draftState: draftState,
inputActivities: inputActivities,
promoInfo: promoInfo,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: hasFailedMessages
),
editing: editing,
hasActiveRevealControls: hasActiveRevealControls,
selected: selected,
header: nil,
enableContextActions: true,
hiddenOffset: false,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters):
let itemPeer = peer.chatMainPeer
var chatPeer: Peer?
var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] {
chatPeer = peer
}
var enabled = true
if filter.contains(.onlyWriteable) {
if let peer = peer.peers[peer.peerId] {
if !canSendMessagesToPeer(peer) {
if !canSendMessagesToPeer(peer._asPeer()) {
enabled = false
}
} else {
@ -233,7 +259,10 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
}
if filter.contains(.onlyPrivateChats) {
if let peer = peer.peers[peer.peerId] {
if !(peer is TelegramUser || peer is TelegramSecretChat) {
switch peer {
case .user, .secretChat:
break
default:
enabled = false
}
} else {
@ -242,8 +271,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
}
if filter.contains(.onlyGroups) {
if let peer = peer.peers[peer.peerId] {
if let _ = peer as? TelegramGroup {
} else if let peer = peer as? TelegramChannel, case .group = peer.info {
if case .legacyGroup = peer {
} else if case let .channel(peer) = peer, case .group = peer.info {
} else {
enabled = false
}
@ -254,7 +283,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
if filter.contains(.onlyManageable) {
if let peer = peer.peers[peer.peerId] {
var canManage = false
if let peer = peer as? TelegramGroup {
if case let .legacyGroup(peer) = peer {
switch peer.role {
case .creator, .admin:
canManage = true
@ -264,7 +293,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
}
if canManage {
} else if let peer = peer as? TelegramChannel, case .group = peer.info, peer.hasPermission(.inviteMembers) {
} else if case let .channel(peer) = peer, case .group = peer.info, peer.hasPermission(.inviteMembers) {
} else {
enabled = false
}
@ -274,7 +303,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
}
if filter.contains(.excludeChannels) {
if let peer = peer.peers[peer.peerId] {
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
if case let .channel(peer) = peer, case .broadcast = peer.info {
enabled = false
}
}
@ -299,57 +328,115 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
var status: ContactsPeerItemStatus = .none
if isSelecting, let itemPeer = itemPeer {
let tagSummaryCount = summaryInfo.tagSummaryCount ?? 0
let actionsSummaryCount = summaryInfo.actionsSummaryCount ?? 0
let totalMentionCount = tagSummaryCount - actionsSummaryCount
if let (string, multiline) = statusStringForPeerType(accountPeerId: context.account.peerId, strings: presentationData.strings, peer: itemPeer, isMuted: isRemovedFromTotalUnreadCount, isUnread: combinedReadState?.isUnread ?? false, isContact: isContact, hasUnseenMentions: totalMentionCount > 0, chatListFilters: filters) {
if let (string, multiline) = statusStringForPeerType(accountPeerId: context.account.peerId, strings: presentationData.strings, peer: itemPeer, isMuted: isRemovedFromTotalUnreadCount, isUnread: combinedReadState?.isUnread ?? false, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: filters) {
status = .custom(string: string, multiline: multiline)
} else {
status = .none
}
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: itemPeer.flatMap(EnginePeer.init), chatPeer: chatPeer.flatMap(EnginePeer.init)), status: status, enabled: enabled, selection: editing ? .selectable(selected: selected) : .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
if let chatPeer = chatPeer {
if editing {
nodeInteraction.togglePeerSelected(chatPeer)
} else {
nodeInteraction.peerSelected(chatPeer, nil, nil)
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings),
sortOrder: presentationData.nameSortOrder,
displayOrder: presentationData.nameDisplayOrder,
context: context,
peerMode: .generalSearch,
peer: .peer(peer: itemPeer, chatPeer: chatPeer),
status: status,
enabled: enabled,
selection: editing ? .selectable(selected: selected) : .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: header,
action: { _ in
if let chatPeer = chatPeer {
if editing {
nodeInteraction.togglePeerSelected(chatPeer)
} else {
nodeInteraction.peerSelected(chatPeer, nil, nil)
}
}
}, disabledAction: { _ in
if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer)
}
}
}, disabledAction: { _ in
if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer)
}
}), directionHint: entry.directionHint)
), directionHint: entry.directionHint)
}
case let .HoleEntry(_, theme):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
case let .GroupReferenceEntry(index, presentationData, groupId, peers, message, editing, unreadState, revealed, hiddenByDefault):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .groupReference(groupId: groupId, peers: peers, message: message, unreadState: unreadState, hiddenByDefault: hiddenByDefault), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, hiddenOffset: hiddenByDefault && !revealed, interaction: nodeInteraction), directionHint: entry.directionHint)
case let .GroupReferenceEntry(index, presentationData, groupId, peers, message, editing, unreadCount, revealed, hiddenByDefault):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: presentationData,
context: context,
peerGroupId: peerGroupId,
filterData: filterData,
index: index,
content: .groupReference(
groupId: groupId,
peers: peers,
message: message,
unreadCount: unreadCount,
hiddenByDefault: hiddenByDefault
),
editing: editing,
hasActiveRevealControls: false,
selected: false,
header: nil,
enableContextActions: true,
hiddenOffset: hiddenByDefault && !revealed,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
}
}
}
private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: EngineChatList.Group, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
return entries.map { entry -> ListViewUpdateItem in
switch entry.entry {
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, presence, hasUnseenMentions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
switch mode {
case .chatList:
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .peer(messages: messages, peer: peer, combinedReadState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: hasFailedMessages), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: presentationData,
context: context,
peerGroupId: peerGroupId,
filterData: filterData,
index: index,
content: .peer(
messages: messages,
peer: peer,
combinedReadState: combinedReadState,
isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount,
presence: presence,
hasUnseenMentions: hasUnseenMentions,
draftState: draftState,
inputActivities: inputActivities,
promoInfo: promoInfo,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: hasFailedMessages
),
editing: editing,
hasActiveRevealControls: hasActiveRevealControls,
selected: selected,
header: nil,
enableContextActions: true,
hiddenOffset: false,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters):
let itemPeer = peer.chatMainPeer
var chatPeer: Peer?
var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] {
chatPeer = peer
}
var enabled = true
if filter.contains(.onlyWriteable) {
if let peer = peer.peers[peer.peerId] {
if !canSendMessagesToPeer(peer) {
if !canSendMessagesToPeer(peer._asPeer()) {
enabled = false
}
} else {
@ -357,7 +444,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
}
}
if filter.contains(.excludeChannels) {
if let peer = peer.chatMainPeer as? TelegramChannel, case .broadcast = peer.info {
if case let .channel(peer) = peer.chatMainPeer, case .broadcast = peer.info {
enabled = false
}
}
@ -380,34 +467,65 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
var status: ContactsPeerItemStatus = .none
if isSelecting, let itemPeer = itemPeer {
let tagSummaryCount = summaryInfo.tagSummaryCount ?? 0
let actionsSummaryCount = summaryInfo.actionsSummaryCount ?? 0
let totalMentionCount = tagSummaryCount - actionsSummaryCount
if let (string, multiline) = statusStringForPeerType(accountPeerId: context.account.peerId, strings: presentationData.strings, peer: itemPeer, isMuted: isRemovedFromTotalUnreadCount, isUnread: combinedReadState?.isUnread ?? false, isContact: isContact, hasUnseenMentions: totalMentionCount > 0, chatListFilters: filters) {
if let (string, multiline) = statusStringForPeerType(accountPeerId: context.account.peerId, strings: presentationData.strings, peer: itemPeer, isMuted: isRemovedFromTotalUnreadCount, isUnread: combinedReadState?.isUnread ?? false, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: filters) {
status = .custom(string: string, multiline: multiline)
} else {
status = .none
}
}
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: itemPeer.flatMap(EnginePeer.init), chatPeer: chatPeer.flatMap(EnginePeer.init)), status: status, enabled: enabled, selection: editing ? .selectable(selected: selected) : .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
if let chatPeer = chatPeer {
if editing {
nodeInteraction.togglePeerSelected(chatPeer)
} else {
nodeInteraction.peerSelected(chatPeer, nil, nil)
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings),
sortOrder: presentationData.nameSortOrder,
displayOrder: presentationData.nameDisplayOrder,
context: context,
peerMode: .generalSearch,
peer: .peer(peer: itemPeer, chatPeer: chatPeer),
status: status,
enabled: enabled,
selection: editing ? .selectable(selected: selected) : .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: header,
action: { _ in
if let chatPeer = chatPeer {
if editing {
nodeInteraction.togglePeerSelected(chatPeer)
} else {
nodeInteraction.peerSelected(chatPeer, nil, nil)
}
}
}, disabledAction: { _ in
if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer)
}
}
}, disabledAction: { _ in
if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer)
}
}), directionHint: entry.directionHint)
), directionHint: entry.directionHint)
}
case let .HoleEntry(_, theme):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
case let .GroupReferenceEntry(index, presentationData, groupId, peers, message, editing, unreadState, revealed, hiddenByDefault):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, context: context, peerGroupId: peerGroupId, filterData: filterData, index: index, content: .groupReference(groupId: groupId, peers: peers, message: message, unreadState: unreadState, hiddenByDefault: hiddenByDefault), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, hiddenOffset: hiddenByDefault && !revealed, interaction: nodeInteraction), directionHint: entry.directionHint)
case let .GroupReferenceEntry(index, presentationData, groupId, peers, message, editing, unreadCount, revealed, hiddenByDefault):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: presentationData,
context: context,
peerGroupId: peerGroupId,
filterData: filterData,
index: index,
content: .groupReference(
groupId: groupId,
peers: peers,
message: message,
unreadCount: unreadCount,
hiddenByDefault: hiddenByDefault
),
editing: editing,
hasActiveRevealControls: false,
selected: false,
header: nil,
enableContextActions: true,
hiddenOffset: hiddenByDefault && !revealed,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case .HeaderEntry:
@ -434,7 +552,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
}
}
private func mappedChatListNodeViewListTransition(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, transition: ChatListNodeViewTransition) -> ChatListNodeListViewTransition {
private func mappedChatListNodeViewListTransition(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: EngineChatList.Group, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, transition: ChatListNodeViewTransition) -> ChatListNodeListViewTransition {
return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, filterData: filterData, mode: mode, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, filterData: filterData, mode: mode, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, adjustScrollToFirstItem: transition.adjustScrollToFirstItem, animateCrossfade: transition.animateCrossfade)
}
@ -449,7 +567,7 @@ private final class ChatListOpaqueTransactionState {
public enum ChatListSelectionOption {
case previous(unread: Bool)
case next(unread: Bool)
case peerId(PeerId)
case peerId(EnginePeer.Id)
case index(Int)
}
@ -473,7 +591,7 @@ public enum ChatListNodeEmptyState: Equatable {
public final class ChatListNode: ListView {
private let fillPreloadItems: Bool
private let context: AccountContext
private let groupId: PeerGroupId
private let groupId: EngineChatList.Group
private let mode: ChatListNodeMode
private let _ready = ValuePromise<Bool>()
@ -488,18 +606,18 @@ public final class ChatListNode: ListView {
return _contentsReady.get()
}
public var peerSelected: ((Peer, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
public var disabledPeerSelected: ((Peer) -> Void)?
public var peerSelected: ((EnginePeer, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
public var disabledPeerSelected: ((EnginePeer) -> Void)?
public var additionalCategorySelected: ((Int) -> Void)?
public var groupSelected: ((PeerGroupId) -> Void)?
public var groupSelected: ((EngineChatList.Group) -> Void)?
public var addContact: ((String) -> Void)?
public var activateSearch: (() -> Void)?
public var deletePeerChat: ((PeerId, Bool) -> Void)?
public var updatePeerGrouping: ((PeerId, Bool) -> Void)?
public var deletePeerChat: ((EnginePeer.Id, Bool) -> Void)?
public var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
public var presentAlert: ((String) -> Void)?
public var present: ((ViewController) -> Void)?
public var toggleArchivedFolderHiddenByDefault: (() -> Void)?
public var hidePsa: ((PeerId) -> Void)?
public var hidePsa: ((EnginePeer.Id) -> Void)?
public var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?) -> Void)?
private var theme: PresentationTheme
@ -569,10 +687,10 @@ public final class ChatListNode: ListView {
var isEmptyUpdated: ((ChatListNodeEmptyState, Bool, ContainedViewLayoutTransition) -> Void)?
private var currentIsEmptyState: ChatListNodeEmptyState?
public var addedVisibleChatsWithPeerIds: (([PeerId]) -> Void)?
public var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
private let currentRemovingPeerId = Atomic<PeerId?>(value: nil)
public func setCurrentRemovingPeerId(_ peerId: PeerId?) {
private let currentRemovingPeerId = Atomic<EnginePeer.Id?>(value: nil)
public func setCurrentRemovingPeerId(_ peerId: EnginePeer.Id?) {
let _ = self.currentRemovingPeerId.swap(peerId)
}
@ -585,7 +703,7 @@ public final class ChatListNode: ListView {
var isSelectionGestureEnabled = true
public init(context: AccountContext, groupId: PeerGroupId, chatListFilter: ChatListFilter? = nil, previewing: Bool, fillPreloadItems: Bool, mode: ChatListNodeMode, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
public init(context: AccountContext, groupId: EngineChatList.Group, chatListFilter: ChatListFilter? = nil, previewing: Bool, fillPreloadItems: Bool, mode: ChatListNodeMode, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.context = context
self.groupId = groupId
self.chatListFilter = chatListFilter
@ -712,7 +830,7 @@ public final class ChatListNode: ListView {
if let chatListFilter = chatListFilter {
location = .filter(chatListFilter.id)
} else {
location = .group(groupId)
location = .group(groupId._asGroup())
}
let _ = (context.engine.peers.toggleItemPinned(location: location, itemId: itemId)
|> deliverOnMainQueue).start(next: { result in
@ -785,7 +903,7 @@ public final class ChatListNode: ListView {
let chatListViewUpdate = self.chatListLocation.get()
|> distinctUntilChanged
|> mapToSignal { location -> Signal<(ChatListNodeViewUpdate, ChatListFilter?), NoError> in
return chatListViewForLocation(groupId: groupId, location: location, account: context.account)
return chatListViewForLocation(groupId: groupId._asGroup(), location: location, account: context.account)
|> map { update in
return (update, location.filter)
}
@ -796,10 +914,13 @@ public final class ChatListNode: ListView {
let previousHideArchivedFolderByDefault = Atomic<Bool?>(value: nil)
let currentRemovingPeerId = self.currentRemovingPeerId
let savedMessagesPeer: Signal<Peer?, NoError>
let savedMessagesPeer: Signal<EnginePeer?, NoError>
if case let .peers(filter, _, _, _) = mode, filter.contains(.onlyWriteable) {
savedMessagesPeer = context.account.postbox.loadedPeerWithId(context.account.peerId)
|> map(Optional.init)
|> map { peer in
return peer.flatMap(EnginePeer.init)
}
} else {
savedMessagesPeer = .single(nil)
}
@ -812,7 +933,7 @@ public final class ChatListNode: ListView {
|> distinctUntilChanged
let displayArchiveIntro: Signal<Bool, NoError>
if Namespaces.PeerGroup.archive == groupId {
if case .archive = groupId {
displayArchiveIntro = context.sharedContext.accountManager.noticeEntry(key: ApplicationSpecificNotice.archiveIntroDismissedKey())
|> map { entry -> Bool in
if let value = entry.value?.get(ApplicationSpecificVariantNotice.self) {
@ -835,7 +956,7 @@ public final class ChatListNode: ListView {
displayArchiveIntro = .single(false)
}
let currentPeerId: PeerId = context.account.peerId
let currentPeerId: EnginePeer.Id = context.account.peerId
let chatListNodeViewTransition = combineLatest(queue: viewProcessingQueue, hideArchivedFolderByDefault, displayArchiveIntro, savedMessagesPeer, chatListViewUpdate, self.statePromise.get())
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in
@ -843,7 +964,7 @@ public final class ChatListNode: ListView {
let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault)
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode)
let (rawEntries, isLoading) = chatListNodeEntriesForView(EngineChatList(update.view), state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode)
let entries = rawEntries.filter { entry in
switch entry {
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _):
@ -858,7 +979,7 @@ public final class ChatListNode: ListView {
if filter.contains(.onlyGroups) {
var isGroup: Bool = false
if let peer = peer.chatMainPeer as? TelegramChannel, case .group = peer.info {
if case let .channel(peer) = peer.chatMainPeer, case .group = peer.info {
isGroup = true
} else if peer.peerId.namespace == Namespaces.Peer.CloudGroup {
isGroup = true
@ -869,7 +990,7 @@ public final class ChatListNode: ListView {
}
if filter.contains(.onlyChannels) {
if let peer = peer.chatMainPeer as? TelegramChannel, case .broadcast = peer.info {
if case let .channel(peer) = peer.chatMainPeer, case .broadcast = peer.info {
return true
} else {
return false
@ -877,13 +998,13 @@ public final class ChatListNode: ListView {
}
if filter.contains(.excludeChannels) {
if let peer = peer.chatMainPeer as? TelegramChannel, case .broadcast = peer.info {
if case let .channel(peer) = peer.chatMainPeer, case .broadcast = peer.info {
}
}
if filter.contains(.onlyWriteable) && filter.contains(.excludeDisabled) {
if let peer = peer.peers[peer.peerId] {
if !canSendMessagesToPeer(peer) {
if !canSendMessagesToPeer(peer._asPeer()) {
return false
}
} else {
@ -950,8 +1071,8 @@ public final class ChatListNode: ListView {
if previousState.editing != state.editing {
disableAnimations = false
} else {
var previousPinnedChats: [PeerId] = []
var updatedPinnedChats: [PeerId] = []
var previousPinnedChats: [EnginePeer.Id] = []
var updatedPinnedChats: [EnginePeer.Id] = []
var didIncludeRemovingPeerId = false
var didIncludeHiddenByDefaultArchive = false
@ -1095,18 +1216,18 @@ public final class ChatListNode: ListView {
self.setChatListLocation(initialLocation)
let postbox = context.account.postbox
let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:])
let previousPeerCache = Atomic<[EnginePeer.Id: EnginePeer]>(value: [:])
let previousActivities = Atomic<ChatListNodePeerInputActivities?>(value: nil)
self.activityStatusesDisposable = (context.account.allPeerInputActivities()
|> mapToSignal { activitiesByPeerId -> Signal<[PeerId: [(Peer, PeerInputActivity)]], NoError> in
|> mapToSignal { activitiesByPeerId -> Signal<[EnginePeer.Id: [(EnginePeer, PeerInputActivity)]], NoError> in
var foundAllPeers = true
var cachedResult: [PeerId: [(Peer, PeerInputActivity)]] = [:]
var cachedResult: [EnginePeer.Id: [(EnginePeer, PeerInputActivity)]] = [:]
previousPeerCache.with { dict -> Void in
for (chatPeerId, activities) in activitiesByPeerId {
guard case .global = chatPeerId.category else {
continue
}
var cachedChatResult: [(Peer, PeerInputActivity)] = []
var cachedChatResult: [(EnginePeer, PeerInputActivity)] = []
for (peerId, activity) in activities {
if let peer = dict[peerId] {
cachedChatResult.append((peer, activity))
@ -1121,19 +1242,19 @@ public final class ChatListNode: ListView {
if foundAllPeers {
return .single(cachedResult)
} else {
return postbox.transaction { transaction -> [PeerId: [(Peer, PeerInputActivity)]] in
var result: [PeerId: [(Peer, PeerInputActivity)]] = [:]
var peerCache: [PeerId: Peer] = [:]
return postbox.transaction { transaction -> [EnginePeer.Id: [(EnginePeer, PeerInputActivity)]] in
var result: [EnginePeer.Id: [(EnginePeer, PeerInputActivity)]] = [:]
var peerCache: [EnginePeer.Id: EnginePeer] = [:]
for (chatPeerId, activities) in activitiesByPeerId {
guard case .global = chatPeerId.category else {
continue
}
var chatResult: [(Peer, PeerInputActivity)] = []
var chatResult: [(EnginePeer, PeerInputActivity)] = []
for (peerId, activity) in activities {
if let peer = transaction.getPeer(peerId) {
chatResult.append((peer, activity))
peerCache[peerId] = peer
chatResult.append((EnginePeer(peer), activity))
peerCache[peerId] = EnginePeer(peer)
}
}
@ -1147,7 +1268,7 @@ public final class ChatListNode: ListView {
|> map { activities -> ChatListNodePeerInputActivities? in
return previousActivities.modify { current in
var updated = false
let currentList: [PeerId: [(Peer, PeerInputActivity)]] = current?.activities ?? [:]
let currentList: [EnginePeer.Id: [(EnginePeer, PeerInputActivity)]] = current?.activities ?? [:]
if currentList.count != activities.count {
updated = true
} else {
@ -1158,7 +1279,7 @@ public final class ChatListNode: ListView {
break outer
} else {
for i in 0 ..< currentValue.count {
if !arePeersEqual(currentValue[i].0, value[i].0) {
if currentValue[i].0 != value[i].0 {
updated = true
break outer
}
@ -1201,7 +1322,7 @@ public final class ChatListNode: ListView {
let fromEntry = filteredEntries[filteredEntries.count - 1 - fromIndex]
let toEntry = filteredEntries[filteredEntries.count - 1 - toIndex]
var referenceId: PinnedItemId?
var referenceId: EngineChatList.PinnedItem.Id?
var beforeAll = false
switch toEntry {
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _):
@ -1219,7 +1340,7 @@ public final class ChatListNode: ListView {
if let chatListFilter = chatListFilter {
location = .filter(chatListFilter.id)
} else {
location = .group(groupId)
location = .group(groupId._asGroup())
}
let engine = strongSelf.context.engine
@ -1227,7 +1348,7 @@ public final class ChatListNode: ListView {
|> mapToSignal { itemIds -> Signal<Bool, NoError> in
var itemIds = itemIds
var itemId: PinnedItemId?
var itemId: EngineChatList.PinnedItem.Id?
switch fromEntry {
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
itemId = .peer(index.messageIndex.id.peerId)
@ -1595,7 +1716,7 @@ public final class ChatListNode: ListView {
isEmptyState = .notEmpty(containsChats: containsChats)
}
var insertedPeerIds: [PeerId] = []
var insertedPeerIds: [EnginePeer.Id] = []
for item in transition.insertItems {
if let item = item.item as? ChatListItem {
switch item.content {
@ -1754,10 +1875,10 @@ public final class ChatListNode: ListView {
self.chatListLocation.set(location)
}
private func relativeUnreadChatListIndex(position: ChatListRelativePosition) -> Signal<ChatListIndex?, NoError> {
private func relativeUnreadChatListIndex(position: EngineChatList.RelativePosition) -> Signal<EngineChatList.Item.Index?, NoError> {
let groupId = self.groupId
let postbox = self.context.account.postbox
return self.context.sharedContext.accountManager.transaction { transaction -> Signal<ChatListIndex?, NoError> in
return self.context.sharedContext.accountManager.transaction { transaction -> Signal<EngineChatList.Item.Index?, NoError> in
var filter = true
if let inAppNotificationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings)?.get(InAppNotificationSettings.self) {
switch inAppNotificationSettings.totalUnreadCountDisplayStyle {
@ -1765,14 +1886,14 @@ public final class ChatListNode: ListView {
filter = true
}
}
return postbox.transaction { transaction -> ChatListIndex? in
return transaction.getRelativeUnreadChatListIndex(filtered: filter, position: position, groupId: groupId)
return postbox.transaction { transaction -> EngineChatList.Item.Index? in
return transaction.getRelativeUnreadChatListIndex(filtered: filter, position: position._asPosition(), groupId: groupId._asGroup())
}
}
|> switchToLatest
}
public func scrollToEarliestUnread(earlierThan: ChatListIndex?) {
public func scrollToEarliestUnread(earlierThan: EngineChatList.Item.Index?) {
let _ = (relativeUnreadChatListIndex(position: .earlier(than: earlierThan)) |> deliverOnMainQueue).start(next: { [weak self] index in
guard let strongSelf = self else {
return
@ -1804,9 +1925,9 @@ public final class ChatListNode: ListView {
}
let entryCount = chatListView.filteredEntries.count
var current: (ChatListIndex, Peer, Int)? = nil
var previous: (ChatListIndex, Peer)? = nil
var next: (ChatListIndex, Peer)? = nil
var current: (EngineChatList.Item.Index, EnginePeer, Int)? = nil
var previous: (EngineChatList.Item.Index, EnginePeer)? = nil
var next: (EngineChatList.Item.Index, EnginePeer)? = nil
outer: for i in range.firstIndex ..< range.lastIndex {
if i < 0 || i >= entryCount {
@ -1826,7 +1947,7 @@ public final class ChatListNode: ListView {
switch option {
case .previous(unread: true), .next(unread: true):
let position: ChatListRelativePosition
let position: EngineChatList.RelativePosition
if let current = current {
if case .previous = option {
position = .earlier(than: current.0)
@ -1838,11 +1959,11 @@ public final class ChatListNode: ListView {
}
let postbox = self.context.account.postbox
let _ = (relativeUnreadChatListIndex(position: position)
|> mapToSignal { index -> Signal<(ChatListIndex, Peer)?, NoError> in
|> mapToSignal { index -> Signal<(EngineChatList.Item.Index, EnginePeer)?, NoError> in
if let index = index {
return postbox.transaction { transaction -> (ChatListIndex, Peer)? in
return transaction.getPeer(index.messageIndex.id.peerId).flatMap { peer -> (ChatListIndex, Peer)? in
(index, peer)
return postbox.transaction { transaction -> (EngineChatList.Item.Index, EnginePeer)? in
return transaction.getPeer(index.messageIndex.id.peerId).flatMap { peer -> (EngineChatList.Item.Index, EnginePeer)? in
(index, EnginePeer(peer))
}
}
} else {
@ -1858,7 +1979,7 @@ public final class ChatListNode: ListView {
strongSelf.peerSelected?(peer, false, false, nil)
})
case .previous(unread: false), .next(unread: false):
var target: (ChatListIndex, Peer)? = nil
var target: (EngineChatList.Item.Index, EnginePeer)? = nil
if let current = current, entryCount > 1 {
if current.2 > 0, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] {
next = (index, peer.peer!)
@ -1882,8 +2003,8 @@ public final class ChatListNode: ListView {
self.peerSelected?(target.1, false, false, nil)
}
case let .peerId(peerId):
let _ = (self.context.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
let _ = (self.context.account.postbox.transaction { transaction -> EnginePeer? in
return transaction.getPeer(peerId).flatMap(EnginePeer.init)
}
|> deliverOnMainQueue).start(next: { [weak self] peer in
guard let strongSelf = self, let peer = peer else {
@ -1901,14 +2022,14 @@ public final class ChatListNode: ListView {
guard let self = self else {
return
}
let _ = (chatListViewForLocation(groupId: self.groupId, location: .initial(count: 10, filter: filter), account: self.context.account)
let _ = (chatListViewForLocation(groupId: self.groupId._asGroup(), location: .initial(count: 10, filter: filter), account: self.context.account)
|> take(1)
|> deliverOnMainQueue).start(next: { update in
let entries = update.view.entries
if entries.count > index, case let .MessageEntry(index, _, _, _, _, renderedPeer, _, _, _, _) = entries[10 - index - 1] {
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true, filter: filter)
self.setChatListLocation(location)
self.peerSelected?(renderedPeer.peer!, false, false, nil)
self.peerSelected?(EnginePeer(renderedPeer.peer!), false, false, nil)
}
})
})
@ -1936,7 +2057,7 @@ public final class ChatListNode: ListView {
}
}
private func currentlyVisibleLatestChatListIndex() -> ChatListIndex? {
private func currentlyVisibleLatestChatListIndex() -> EngineChatList.Item.Index? {
guard let chatListView = (self.opaqueTransactionState as? ChatListOpaqueTransactionState)?.chatListView else {
return nil
}
@ -1958,8 +2079,8 @@ public final class ChatListNode: ListView {
return nil
}
private func peerAtPoint(_ point: CGPoint) -> Peer? {
var resultPeer: Peer?
private func peerAtPoint(_ point: CGPoint) -> EnginePeer? {
var resultPeer: EnginePeer?
self.forEachVisibleItemNode { itemNode in
if resultPeer == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) {
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
@ -1975,7 +2096,7 @@ public final class ChatListNode: ListView {
return resultPeer
}
private var selectionPanState: (selecting: Bool, initialPeerId: PeerId, toggledPeerIds: [[PeerId]])?
private var selectionPanState: (selecting: Bool, initialPeerId: EnginePeer.Id, toggledPeerIds: [[EnginePeer.Id]])?
private var selectionScrollActivationTimer: SwiftSignalKit.Timer?
private var selectionScrollDisplayLink: ConstantDisplayLinkAnimator?
private var selectionScrollDelta: CGFloat?
@ -2024,7 +2145,7 @@ public final class ChatListNode: ListView {
self.selectionPanState = (state.selecting, state.initialPeerId, [])
}
} else if state.toggledPeerIds.last?.first != peer.id {
var updatedToggledPeerIds: [[PeerId]] = []
var updatedToggledPeerIds: [[EnginePeer.Id]] = []
var previouslyToggled = false
for i in (0 ..< state.toggledPeerIds.count) {
if let peerId = state.toggledPeerIds[i].first {
@ -2100,7 +2221,7 @@ public final class ChatListNode: ListView {
}
}
private func statusStringForPeerType(accountPeerId: PeerId, strings: PresentationStrings, peer: Peer, isMuted: Bool, isUnread: Bool, isContact: Bool, hasUnseenMentions: Bool, chatListFilters: [ChatListFilter]?) -> (String, Bool)? {
private func statusStringForPeerType(accountPeerId: EnginePeer.Id, strings: PresentationStrings, peer: EnginePeer, isMuted: Bool, isUnread: Bool, isContact: Bool, hasUnseenMentions: Bool, chatListFilters: [ChatListFilter]?) -> (String, Bool)? {
if accountPeerId == peer.id {
return nil
}
@ -2109,7 +2230,7 @@ private func statusStringForPeerType(accountPeerId: PeerId, strings: Presentatio
var result = ""
for filter in chatListFilters {
let predicate = chatListFilterPredicate(filter: filter.data)
if predicate.includes(peer: peer, groupId: .root, isRemovedFromTotalUnreadCount: isMuted, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: hasUnseenMentions) {
if predicate.includes(peer: peer._asPeer(), groupId: .root, isRemovedFromTotalUnreadCount: isMuted, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: hasUnseenMentions) {
if !result.isEmpty {
result.append(", ")
}
@ -2126,7 +2247,7 @@ private func statusStringForPeerType(accountPeerId: PeerId, strings: Presentatio
if peer.id.isReplies {
return nil
} else if let user = peer as? TelegramUser {
} else if case let .user(user) = peer {
if user.botInfo != nil || user.flags.contains(.isSupport) {
return (strings.ChatList_PeerTypeBot, false)
} else if isContact {
@ -2134,15 +2255,15 @@ private func statusStringForPeerType(accountPeerId: PeerId, strings: Presentatio
} else {
return (strings.ChatList_PeerTypeNonContact, false)
}
} else if peer is TelegramSecretChat {
} else if case .secretChat = peer {
if isContact {
return (strings.ChatList_PeerTypeContact, false)
} else {
return (strings.ChatList_PeerTypeNonContact, false)
}
} else if peer is TelegramGroup {
} else if case .legacyGroup = peer {
return (strings.ChatList_PeerTypeGroup, false)
} else if let channel = peer as? TelegramChannel {
} else if case let .channel(channel) = peer {
if case .group = channel.info {
return (strings.ChatList_PeerTypeGroup, false)
} else {

View File

@ -1,6 +1,5 @@
import Foundation
import UIKit
import Postbox
import TelegramCore
import TelegramPresentationData
import MergeLists
@ -10,13 +9,13 @@ enum ChatListNodeEntryId: Hashable {
case Header
case Hole(Int64)
case PeerId(Int64)
case GroupId(PeerGroupId)
case GroupId(EngineChatList.Group)
case ArchiveIntro
case additionalCategory(Int)
}
enum ChatListNodeEntrySortIndex: Comparable {
case index(ChatListIndex)
case index(EngineChatList.Item.Index)
case additionalCategory(Int)
static func <(lhs: ChatListNodeEntrySortIndex, rhs: ChatListNodeEntrySortIndex) -> Bool {
@ -46,24 +45,24 @@ public enum ChatListNodeEntryPromoInfo: Equatable {
enum ChatListNodeEntry: Comparable, Identifiable {
case HeaderEntry
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool)
case HoleEntry(ChatListHole, theme: PresentationTheme)
case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, editing: Bool, unreadState: PeerGroupUnreadCountersCombinedSummary, revealed: Bool, hiddenByDefault: Bool)
case PeerEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, messages: [EngineMessage], readState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, draftState: ChatListItemContent.DraftState?, peer: EngineRenderedPeer, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool)
case HoleEntry(EngineMessage.Index, theme: PresentationTheme)
case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool)
case ArchiveIntro(presentationData: ChatListPresentationData)
case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData)
var sortIndex: ChatListNodeEntrySortIndex {
switch self {
case .HeaderEntry:
return .index(ChatListIndex.absoluteUpperBound)
return .index(EngineChatList.Item.Index.absoluteUpperBound)
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return .index(index)
case let .HoleEntry(hole, _):
return .index(ChatListIndex(pinningIndex: nil, messageIndex: hole.index))
case let .HoleEntry(holeIndex, _):
return .index(EngineChatList.Item.Index(pinningIndex: nil, messageIndex: holeIndex))
case let .GroupReferenceEntry(index, _, _, _, _, _, _, _, _):
return .index(index)
case .ArchiveIntro:
return .index(ChatListIndex.absoluteUpperBound.successor)
return .index(EngineChatList.Item.Index.absoluteUpperBound.successor)
case let .AdditionalCategory(index, _, _, _, _, _, _):
return .additionalCategory(index)
}
@ -75,8 +74,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return .Header
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return .PeerId(index.messageIndex.id.peerId.toInt64())
case let .HoleEntry(hole, _):
return .Hole(Int64(hole.index.id.id))
case let .HoleEntry(holeIndex, _):
return .Hole(Int64(holeIndex.id.id))
case let .GroupReferenceEntry(_, _, groupId, _, _, _, _, _, _):
return .GroupId(groupId)
case .ArchiveIntro:
@ -137,7 +136,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return false
}
if let lhsPeerPresence = lhsPresence, let rhsPeerPresence = rhsPresence {
if !lhsPeerPresence.isEqual(to: rhsPeerPresence) {
if lhsPeerPresence != rhsPeerPresence {
return false
}
} else if (lhsPresence != nil) != (rhsPresence != nil) {
@ -170,7 +169,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return false
}
for i in 0 ..< lhsInputActivities.count {
if !arePeersEqual(lhsInputActivities[i].0, rhsInputActivities[i].0) {
if lhsInputActivities[i].0 != rhsInputActivities[i].0 {
return false
}
if lhsInputActivities[i].1 != rhsInputActivities[i].1 {
@ -273,115 +272,149 @@ enum ChatListNodeEntry: Comparable, Identifiable {
}
}
private func offsetPinnedIndex(_ index: ChatListIndex, offset: UInt16) -> ChatListIndex {
private func offsetPinnedIndex(_ index: EngineChatList.Item.Index, offset: UInt16) -> EngineChatList.Item.Index {
if let pinningIndex = index.pinningIndex {
return ChatListIndex(pinningIndex: pinningIndex + offset, messageIndex: index.messageIndex)
return EngineChatList.Item.Index(pinningIndex: pinningIndex + offset, messageIndex: index.messageIndex)
} else {
return index
}
}
func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, savedMessagesPeer: Peer?, foundPeers: [(Peer, Peer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) {
func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) {
var result: [ChatListNodeEntry] = []
var pinnedIndexOffset: UInt16 = 0
if view.laterIndex == nil, case .chatList = mode {
if !view.hasLater, case .chatList = mode {
var groupEntryCount = 0
for _ in view.groupEntries {
for _ in view.groupItems {
groupEntryCount += 1
}
pinnedIndexOffset += UInt16(groupEntryCount)
}
let filteredAdditionalItemEntries = view.additionalItemEntries.filter { item -> Bool in
return item.info.peerId != state.hiddenPsaPeerId
let filteredAdditionalItemEntries = view.additionalItems.filter { item -> Bool in
return item.item.renderedPeer.peerId != state.hiddenPsaPeerId
}
var foundPeerIds = Set<PeerId>()
var foundPeerIds = Set<EnginePeer.Id>()
for peer in foundPeers {
foundPeerIds.insert(peer.0.id)
}
if view.laterIndex == nil && savedMessagesPeer == nil {
if !view.hasLater && savedMessagesPeer == nil {
pinnedIndexOffset += UInt16(filteredAdditionalItemEntries.count)
}
var filterAfterHole = false
loop: for entry in view.entries {
switch entry {
case let .MessageEntry(index, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
if let savedMessagesPeer = savedMessagesPeer, savedMessagesPeer.id == index.messageIndex.id.peerId || foundPeerIds.contains(index.messageIndex.id.peerId) {
continue loop
}
if state.pendingRemovalPeerIds.contains(index.messageIndex.id.peerId) {
continue loop
}
var updatedMessages = messages
var updatedCombinedReadState = combinedReadState
if state.pendingClearHistoryPeerIds.contains(index.messageIndex.id.peerId) {
updatedMessages = []
updatedCombinedReadState = nil
}
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, peer: peer, presence: peerPresence, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], promoInfo: nil, hasFailedMessages: hasFailed, isContact: isContact))
case let .HoleEntry(hole):
if hole.index.timestamp == Int32.max - 1 {
//return ([.HeaderEntry], true)
}
filterAfterHole = true
result.append(.HoleEntry(hole, theme: state.presentationData.theme))
loop: for entry in view.items {
//case let .MessageEntry(index, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
if let savedMessagesPeer = savedMessagesPeer, savedMessagesPeer.id == entry.index.messageIndex.id.peerId || foundPeerIds.contains(entry.index.messageIndex.id.peerId) {
continue loop
}
if state.pendingRemovalPeerIds.contains(entry.index.messageIndex.id.peerId) {
continue loop
}
var updatedMessages = entry.messages
var updatedCombinedReadState = entry.readCounters
if state.pendingClearHistoryPeerIds.contains(entry.index.messageIndex.id.peerId) {
updatedMessages = []
updatedCombinedReadState = nil
}
var draftState: ChatListItemContent.DraftState?
if let draftText = entry.draftText {
draftState = ChatListItemContent.DraftState(text: draftText)
}
result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, editing: state.editing, hasActiveRevealControls: entry.index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(entry.index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[entry.index.messageIndex.id.peerId], promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact))
}
if view.laterIndex == nil {
if !view.hasLater {
var pinningIndex: UInt16 = UInt16(pinnedIndexOffset == 0 ? 0 : (pinnedIndexOffset - 1))
if let savedMessagesPeer = savedMessagesPeer {
if !foundPeers.isEmpty {
var foundPinningIndex: UInt16 = UInt16(foundPeers.count)
for peer in foundPeers.reversed() {
var peers: [PeerId: Peer] = [peer.0.id: peer.0]
var peers: [EnginePeer.Id: EnginePeer] = [peer.0.id: peer.0]
if let chatPeer = peer.1 {
peers[chatPeer.id] = chatPeer
}
let messageIndex = MessageIndex(id: MessageId(peerId: peer.0.id, namespace: 0, id: 0), timestamp: 1)
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: foundPinningIndex, messageIndex: messageIndex), presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: peer.0.id, peers: SimpleDictionary(peers)), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(peer.0.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false))
let messageIndex = EngineMessage.Index(id: EngineMessage.Id(peerId: peer.0.id, namespace: 0, id: 0), timestamp: 1)
result.append(.PeerEntry(
index: EngineChatList.Item.Index(pinningIndex: foundPinningIndex, messageIndex: messageIndex),
presentationData: state.presentationData,
messages: [],
readState: nil,
isRemovedFromTotalUnreadCount: false,
draftState: nil,
peer: EngineRenderedPeer(peerId: peer.0.id, peers: peers),
presence: nil,
hasUnseenMentions: false,
editing: state.editing,
hasActiveRevealControls: false,
selected: state.selectedPeerIds.contains(peer.0.id),
inputActivities: nil,
promoInfo: nil,
hasFailedMessages: false,
isContact: false
))
if foundPinningIndex != 0 {
foundPinningIndex -= 1
}
}
}
result.append(.PeerEntry(index: ChatListIndex.absoluteUpperBound.predecessor, presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: savedMessagesPeer.id, peers: SimpleDictionary([savedMessagesPeer.id: savedMessagesPeer])), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(savedMessagesPeer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false))
result.append(.PeerEntry(index: EngineChatList.Item.Index.absoluteUpperBound.predecessor, presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, draftState: nil, peer: EngineRenderedPeer(peerId: savedMessagesPeer.id, peers: [savedMessagesPeer.id: savedMessagesPeer]), presence: nil, hasUnseenMentions: false, editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(savedMessagesPeer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false))
} else {
if !filteredAdditionalItemEntries.isEmpty {
for item in filteredAdditionalItemEntries.reversed() {
guard let info = item.info as? PromoChatListItem else {
continue
}
let promoInfo: ChatListNodeEntryPromoInfo
switch info.kind {
switch item.promoInfo.content {
case .proxy:
promoInfo = .proxy
case let .psa(type, message):
promoInfo = .psa(type: type, message: message)
}
switch item.entry {
case let .MessageEntry(index, messages, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
result.append(.PeerEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: index.messageIndex), presentationData: state.presentationData, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, peer: peer, presence: peerPresence, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], promoInfo: promoInfo, hasFailedMessages: hasFailed, isContact: isContact))
if pinningIndex != 0 {
pinningIndex -= 1
}
default:
break
let draftState = item.item.draftText.flatMap(ChatListItemContent.DraftState.init(text:))
result.append(.PeerEntry(
index: EngineChatList.Item.Index(pinningIndex: pinningIndex, messageIndex: item.item.index.messageIndex),
presentationData: state.presentationData,
messages: item.item.messages,
readState: item.item.readCounters,
isRemovedFromTotalUnreadCount: item.item.isMuted,
draftState: draftState,
peer: item.item.renderedPeer,
presence: item.item.presence,
hasUnseenMentions: item.item.hasUnseenMentions,
editing: state.editing,
hasActiveRevealControls: item.item.index.messageIndex.id.peerId == state.peerIdWithRevealedOptions,
selected: state.selectedPeerIds.contains(item.item.index.messageIndex.id.peerId),
inputActivities: state.peerInputActivities?.activities[item.item.index.messageIndex.id.peerId],
promoInfo: promoInfo,
hasFailedMessages: item.item.hasFailed,
isContact: item.item.isContact
))
if pinningIndex != 0 {
pinningIndex -= 1
}
}
}
}
if view.laterIndex == nil, case .chatList = mode {
for groupReference in view.groupEntries {
let messageIndex = MessageIndex(id: MessageId(peerId: PeerId(0), namespace: 0, id: 0), timestamp: 1)
result.append(.GroupReferenceEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: messageIndex), presentationData: state.presentationData, groupId: groupReference.groupId, peers: groupReference.renderedPeers, message: groupReference.message, editing: state.editing, unreadState: groupReference.unreadState, revealed: state.archiveShouldBeTemporaryRevealed, hiddenByDefault: hideArchivedFolderByDefault))
if !view.hasLater, case .chatList = mode {
for groupReference in view.groupItems {
let messageIndex = EngineMessage.Index(id: EngineMessage.Id(peerId: EnginePeer.Id(0), namespace: 0, id: 0), timestamp: 1)
result.append(.GroupReferenceEntry(
index: EngineChatList.Item.Index(pinningIndex: pinningIndex, messageIndex: messageIndex),
presentationData: state.presentationData,
groupId: groupReference.id,
peers: groupReference.items,
message: groupReference.topMessage,
editing: state.editing,
unreadCount: groupReference.unreadCount,
revealed: state.archiveShouldBeTemporaryRevealed,
hiddenByDefault: hideArchivedFolderByDefault
))
if pinningIndex != 0 {
pinningIndex -= 1
}
@ -394,7 +427,7 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
result.append(.HeaderEntry)
}
if view.laterIndex == nil, case let .peers(_, _, additionalCategories,
if !view.hasLater, case let .peers(_, _, additionalCategories,
_) = mode {
var index = 0
for category in additionalCategories.reversed(){
@ -404,34 +437,10 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
}
}
var isLoading: Bool = false
if filterAfterHole {
var seenHole = false
for i in (0 ..< result.count).reversed() {
if seenHole {
result.remove(at: i)
} else {
switch result[i] {
case .HeaderEntry:
break
case .ArchiveIntro, .AdditionalCategory, .GroupReferenceEntry:
break
case .PeerEntry:
break
case .HoleEntry:
isLoading = true
seenHole = true
result.remove(at: i)
}
}
}
}
if result.count >= 1, case .HoleEntry = result[result.count - 1] {
return ([.HeaderEntry], true)
} else if result.count == 1, case .HoleEntry = result[0] {
return ([.HeaderEntry], true)
}
return (result, isLoading)
return (result, view.isLoading)
}

View File

@ -1,7 +1,6 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Postbox
import TelegramCore
import Display
import SwiftSignalKit
@ -20,7 +19,7 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
self.addSubnode(self.activityNode)
}
func asyncLayout() -> (CGSize, ChatListPresentationData, UIColor, PeerId, [(Peer, PeerInputActivity)]) -> (CGSize, () -> Void) {
func asyncLayout() -> (CGSize, ChatListPresentationData, UIColor, EnginePeer.Id, [(EnginePeer, PeerInputActivity)]) -> (CGSize, () -> Void) {
return { [weak self] boundingSize, presentationData, color, peerId, activities in
let strings = presentationData.strings
@ -87,7 +86,7 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
} else {
let text: String
if let _ = commonKey {
let peerTitle = EnginePeer(activities[0].0).compactDisplayTitle
let peerTitle = activities[0].0.compactDisplayTitle
switch activities[0].1 {
case .uploadingVideo:
text = strings.DialogList_SingleUploadingVideoSuffix(peerTitle).string
@ -111,7 +110,7 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
text = ""
}
} else {
text = EnginePeer(activities[0].0).compactDisplayTitle
text = activities[0].0.compactDisplayTitle
}
let string = NSAttributedString(string: text, font: textFont, textColor: color)
@ -137,9 +136,9 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
} else {
let string: NSAttributedString
if activities.count > 1 {
let peerTitle = EnginePeer(activities[0].0).compactDisplayTitle
let peerTitle = activities[0].0.compactDisplayTitle
if activities.count == 2 {
let secondPeerTitle = EnginePeer(activities[1].0).compactDisplayTitle
let secondPeerTitle = activities[1].0.compactDisplayTitle
string = NSAttributedString(string: strings.DialogList_MultipleTypingPair(peerTitle, secondPeerTitle).string, font: textFont, textColor: color)
} else {
string = NSAttributedString(string: strings.DialogList_MultipleTyping(peerTitle, strings.DialogList_MultipleTypingSuffix(activities.count - 1).string).string, font: textFont, textColor: color)

View File

@ -1097,7 +1097,7 @@ public final class ContactListNode: ASDisplayNode {
if globalSearch {
foundRemoteContacts = foundRemoteContacts
|> then(
context.engine.peers.searchPeers(query: query)
context.engine.contacts.searchRemotePeers(query: query)
|> map { ($0.0, $0.1) }
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
)

View File

@ -299,7 +299,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
if categories.contains(.global) {
foundRemoteContacts = .single(previousFoundRemoteContacts.with({ $0 }))
|> then(
context.engine.peers.searchPeers(query: query)
context.engine.contacts.searchRemotePeers(query: query)
|> map { ($0.0, $0.1) }
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
)

View File

@ -13,7 +13,6 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramStringFormatting:TelegramStringFormatting",

View File

@ -3,7 +3,6 @@ import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import TelegramPresentationData
import TelegramStringFormatting
import SearchBarNode

View File

@ -2,7 +2,6 @@ import Foundation
import UIKit
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramStringFormatting
@ -79,21 +78,26 @@ func localizedCountryNamesAndCodes(strings: PresentationStrings) -> [((String, S
return result
}
private func stringTokens(_ string: String) -> [ValueBoxKey] {
private func stringTokens(_ string: String) -> [Data] {
let nsString = string.replacingOccurrences(of: ".", with: "").folding(options: .diacriticInsensitive, locale: .current).lowercased() as NSString
let flag = UInt(kCFStringTokenizerUnitWord)
let tokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, nsString, CFRangeMake(0, nsString.length), flag, CFLocaleCopyCurrent())
var tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)
var tokens: [ValueBoxKey] = []
var tokens: [Data] = []
var addedTokens = Set<ValueBoxKey>()
var addedTokens = Set<Data>()
while tokenType != [] {
let currentTokenRange = CFStringTokenizerGetCurrentTokenRange(tokenizer)
if currentTokenRange.location >= 0 && currentTokenRange.length != 0 {
let token = ValueBoxKey(length: currentTokenRange.length * 2)
nsString.getCharacters(token.memory.assumingMemoryBound(to: unichar.self), range: NSMakeRange(currentTokenRange.location, currentTokenRange.length))
var token = Data(count: currentTokenRange.length * 2)
token.withUnsafeMutableBytes { bytes -> Void in
guard let baseAddress = bytes.baseAddress else {
return
}
nsString.getCharacters(baseAddress.assumingMemoryBound(to: unichar.self), range: NSMakeRange(currentTokenRange.location, currentTokenRange.length))
}
if !addedTokens.contains(token) {
tokens.append(token)
addedTokens.insert(token)
@ -105,13 +109,33 @@ private func stringTokens(_ string: String) -> [ValueBoxKey] {
return tokens
}
private func matchStringTokens(_ tokens: [ValueBoxKey], with other: [ValueBoxKey]) -> Bool {
public func isPrefix(data: Data, to otherData: Data) -> Bool {
if data.isEmpty {
return true
} else if data.count <= otherData.count {
return data.withUnsafeBytes { bytes -> Bool in
guard let bytesBaseAddress = bytes.baseAddress else {
return false
}
return otherData.withUnsafeBytes { otherBytes -> Bool in
guard let otherBytesBaseAddress = otherBytes.baseAddress else {
return false
}
return memcmp(bytesBaseAddress, otherBytesBaseAddress, bytes.count) == 0
}
}
} else {
return false
}
}
private func matchStringTokens(_ tokens: [Data], with other: [Data]) -> Bool {
if other.isEmpty {
return false
} else if other.count == 1 {
let otherToken = other[0]
for token in tokens {
if otherToken.isPrefix(to: token) {
if isPrefix(data: otherToken, to: token) {
return true
}
}
@ -119,7 +143,7 @@ private func matchStringTokens(_ tokens: [ValueBoxKey], with other: [ValueBoxKey
for otherToken in other {
var found = false
for token in tokens {
if otherToken.isPrefix(to: token) {
if isPrefix(data: otherToken, to: token) {
found = true
break
}

View File

@ -13,7 +13,6 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AccountContext:AccountContext",

View File

@ -2,7 +2,6 @@ import Foundation
import UIKit
import Display
import TelegramCore
import Postbox
import SwiftSignalKit
import TelegramPresentationData
import TelegramBaseController
@ -14,7 +13,7 @@ public final class HashtagSearchController: TelegramBaseController {
private let queue = Queue()
private let context: AccountContext
private let peer: Peer?
private let peer: EnginePeer?
private let query: String
private var transitionDisposable: Disposable?
private let openMessageFromSearchDisposable = MetaDisposable()
@ -25,7 +24,7 @@ public final class HashtagSearchController: TelegramBaseController {
return self.displayNode as! HashtagSearchControllerNode
}
public init(context: AccountContext, peer: Peer?, query: String) {
public init(context: AccountContext, peer: EnginePeer?, query: String) {
self.context = context
self.peer = peer
self.query = query
@ -45,7 +44,7 @@ public final class HashtagSearchController: TelegramBaseController {
let search = context.engine.messages.searchMessages(location: location, query: query, state: nil)
let foundMessages: Signal<[ChatListSearchEntry], NoError> = search
|> map { result, _ in
return result.messages.map({ .message($0, RenderedPeer(message: $0), result.readStates[$0.id.peerId], chatListPresentationData, result.totalCount, nil, false) })
return result.messages.map({ .message(EngineMessage($0), EngineRenderedPeer(message: EngineMessage($0)), result.readStates[$0.id.peerId].flatMap(EnginePeerReadCounters.init), chatListPresentationData, result.totalCount, nil, false) })
}
let interaction = ChatListNodeInteraction(activateSearch: {
}, peerSelected: { _, _, _ in
@ -55,7 +54,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) |> deliverOnMainQueue).start(next: { actualPeerId in
strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer()) |> 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(actualPeerId), subject: message.id.peerId == actualPeerId ? .message(id: message.id, highlight: true, timecode: nil) : nil, keepStack: .always))
}

View File

@ -1,7 +1,6 @@
import Display
import UIKit
import AsyncDisplayKit
import Postbox
import TelegramCore
import TelegramPresentationData
import AccountContext
@ -25,7 +24,7 @@ final class HashtagSearchControllerNode: ASDisplayNode {
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
private var hasValidLayout = false
init(context: AccountContext, peer: Peer?, query: String, theme: PresentationTheme, strings: PresentationStrings, navigationBar: NavigationBar?, navigationController: NavigationController?) {
init(context: AccountContext, peer: EnginePeer?, query: String, theme: PresentationTheme, strings: PresentationStrings, navigationBar: NavigationBar?, navigationController: NavigationController?) {
self.navigationBar = navigationBar
self.context = context
@ -48,7 +47,7 @@ final class HashtagSearchControllerNode: ASDisplayNode {
} else if let id = peer?.id, id.isReplies {
items.append(presentationData.strings.DialogList_Replies)
} else {
items.append(peer.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "")
items.append(peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "")
}
items.append(strings.HashtagSearch_AllChats)
self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: 0)

View File

@ -681,7 +681,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
foundRemotePeers = .single(([], []))
} else {
foundContacts = context.account.postbox.searchContacts(query: query.lowercased())
foundRemotePeers = .single(([], [])) |> then(context.engine.peers.searchPeers(query: query)
foundRemotePeers = .single(([], [])) |> then(context.engine.contacts.searchRemotePeers(query: query)
|> delay(0.2, queue: Queue.concurrentDefaultQueue()))
}
case .searchMembers, .searchBanned, .searchKicked, .searchAdmins:
@ -995,7 +995,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
}
if mode == .banAndPromoteActions || mode == .inviteActions {
foundRemotePeers = .single(([], [])) |> then(context.engine.peers.searchPeers(query: query)
foundRemotePeers = .single(([], [])) |> then(context.engine.contacts.searchRemotePeers(query: query)
|> delay(0.2, queue: Queue.concurrentDefaultQueue()))
} else {
foundRemotePeers = .single(([], []))

View File

@ -5,7 +5,6 @@ private enum MetadataKey: Int32 {
case State = 2
case TransactionStateVersion = 3
case MasterClientId = 4
case AccessChallenge = 5
case RemoteContactCount = 6
}
@ -107,14 +106,6 @@ final class MetadataTable: Table {
self.valueBox.set(self.table, key: self.key(.MasterClientId), value: buffer)
}
func accessChallengeData() -> PostboxAccessChallengeData {
if let value = self.valueBox.get(self.table, key: self.key(.AccessChallenge)) {
return PostboxAccessChallengeData(decoder: PostboxDecoder(buffer: value))
} else {
return .none
}
}
func setRemoteContactCount(_ count: Int32) {
self.cachedRemoteContactCount = count
var mutableCount: Int32 = count

View File

@ -29,15 +29,19 @@ private struct CachedEntry {
let entry: CodableEntry?
}
final class NoticeTable: Table {
public final class NoticeTable: Table {
private var cachedEntries: [NoticeEntryKey: CachedEntry] = [:]
private var updatedEntryKeys = Set<NoticeEntryKey>()
static func tableSpec(_ id: Int32) -> ValueBoxTable {
public static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
}
func getAll() -> [ValueBoxKey: CodableEntry] {
public override init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool) {
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
}
public func getAll() -> [ValueBoxKey: CodableEntry] {
var result: [ValueBoxKey: CodableEntry] = [:]
self.valueBox.scan(self.table, values: { key, value in
let object = CodableEntry(data: value.makeData())
@ -47,7 +51,7 @@ final class NoticeTable: Table {
return result
}
func get(key: NoticeEntryKey) -> CodableEntry? {
public func get(key: NoticeEntryKey) -> CodableEntry? {
if let cached = self.cachedEntries[key] {
return cached.entry
} else {
@ -62,12 +66,12 @@ final class NoticeTable: Table {
}
}
func set(key: NoticeEntryKey, value: CodableEntry?) {
public func set(key: NoticeEntryKey, value: CodableEntry?) {
self.cachedEntries[key] = CachedEntry(entry: value)
updatedEntryKeys.insert(key)
}
func clear() {
public func clear() {
var keys: [ValueBoxKey] = []
self.valueBox.scan(self.table, keys: { key in
keys.append(key)
@ -80,11 +84,11 @@ final class NoticeTable: Table {
self.cachedEntries.removeAll()
}
override func clearMemoryCache() {
override public func clearMemoryCache() {
assert(self.updatedEntryKeys.isEmpty)
}
override func beforeCommit() {
override public func beforeCommit() {
if !self.updatedEntryKeys.isEmpty {
for key in self.updatedEntryKeys {
if let value = self.cachedEntries[key]?.entry {

View File

@ -886,15 +886,6 @@ public final class Transaction {
return count
}
public func legacyGetAccessChallengeData() -> PostboxAccessChallengeData {
assert(!self.disposed)
if let postbox = self.postbox {
return postbox.metadataTable.accessChallengeData()
} else {
return .none
}
}
public func enumerateMedia(lowerBound: MessageIndex?, upperBound: MessageIndex?, limit: Int) -> ([PeerId: Set<MediaId>], [MediaId: Media], MessageIndex?) {
assert(!self.disposed)
if let postbox = self.postbox {

View File

@ -6,6 +6,6 @@ public func setPostboxLogger(_ f: @escaping (String) -> Void) {
postboxLogger = f
}
func postboxLog(_ what: @autoclosure () -> String) {
public func postboxLog(_ what: @autoclosure () -> String) {
postboxLogger(what())
}

View File

@ -1,6 +1,6 @@
import Foundation
final public class AdaptedPostboxDecoder {
public final class AdaptedPostboxDecoder {
enum ContentType {
case object
case int32Array

View File

@ -176,7 +176,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])

View File

@ -215,38 +215,136 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
}, activateChatPreview: { _, _, gesture in
gesture?.cancel()
}, present: { _ in })
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
let selfPeer = TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer3 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil)
let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer5 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil)
let peer6 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
func makeChatListItem(
peer: EnginePeer,
author: EnginePeer,
timestamp: Int32,
text: String,
isPinned: Bool = false,
presenceTimestamp: Int32? = nil,
hasInputActivity: Bool = false,
unreadCount: Int32 = 0
) -> ChatListItem {
return ChatListItem(
presentationData: chatListPresentationData,
context: self.context,
peerGroupId: .root,
filterData: nil,
index: ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp)),
content: .peer(
messages: [
EngineMessage(
stableId: 0,
stableVersion: 0,
id: EngineMessage.Id(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0),
globallyUniqueId: nil,
groupingKey: nil,
groupInfo: nil,
threadId: nil,
timestamp: timestamp,
flags: author.id == peer.id ? [.Incoming] : [],
tags: [],
globalTags: [],
localTags: [],
forwardInfo: nil,
author: author,
text: text,
attributes: [],
media: [],
peers: [:],
associatedMessages: [:],
associatedMessageIds: []
)
],
peer: EngineRenderedPeer(peer: peer),
combinedReadState: EnginePeerReadCounters(incomingReadId: 1000, outgoingReadId: 1000, count: unreadCount, markedUnread: false),
isRemovedFromTotalUnreadCount: false,
presence: presenceTimestamp.flatMap { presenceTimestamp in
EnginePeer.Presence(status: .present(until: presenceTimestamp + 1000), lastActivity: presenceTimestamp)
},
hasUnseenMentions: false,
draftState: nil,
inputActivities: hasInputActivity ? [(author, .typingText)] : [],
promoInfo: nil,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: false
),
editing: false,
hasActiveRevealControls: false,
selected: false,
header: nil,
enableContextActions: false,
hiddenOffset: false,
interaction: interaction
)
}
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil))
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil))
let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer1,
author: selfPeer,
timestamp: timestamp1,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text,
isPinned: true
))
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
let timestamp2 = timestamp + 3660
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer2), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer2,
author: peer2,
timestamp: timestamp2,
text: "",
presenceTimestamp: presenceTimestamp,
hasInputActivity: true
))
let timestamp3 = timestamp + 3200
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer3,
author: peer3Author,
timestamp: timestamp3,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text
))
let timestamp4 = timestamp + 3000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer4,
author: peer4,
timestamp: timestamp4,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text
))
let timestamp5 = timestamp + 1000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer5), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer5,
author: peer5,
timestamp: timestamp5,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text
))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer6,
author: peer6,
timestamp: timestamp - 360,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text,
unreadCount: 1
))
let width: CGFloat
if case .regular = layout.metrics.widthClass {
@ -321,7 +419,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])

View File

@ -836,31 +836,116 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
gesture?.cancel()
}, present: { _ in
})
let chatListPresentationData = ChatListPresentationData(theme: self.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
let selfPeer = TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer3 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil)
let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
func makeChatListItem(
peer: EnginePeer,
author: EnginePeer,
timestamp: Int32,
text: String,
isPinned: Bool = false,
presenceTimestamp: Int32? = nil,
hasInputActivity: Bool = false,
unreadCount: Int32 = 0
) -> ChatListItem {
return ChatListItem(
presentationData: chatListPresentationData,
context: self.context,
peerGroupId: .root,
filterData: nil,
index: ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp)),
content: .peer(
messages: [
EngineMessage(
stableId: 0,
stableVersion: 0,
id: EngineMessage.Id(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0),
globallyUniqueId: nil,
groupingKey: nil,
groupInfo: nil,
threadId: nil,
timestamp: timestamp,
flags: author.id == peer.id ? [.Incoming] : [],
tags: [],
globalTags: [],
localTags: [],
forwardInfo: nil,
author: author,
text: text,
attributes: [],
media: [],
peers: [:],
associatedMessages: [:],
associatedMessageIds: []
)
],
peer: EngineRenderedPeer(peer: peer),
combinedReadState: EnginePeerReadCounters(incomingReadId: 1000, outgoingReadId: 1000, count: unreadCount, markedUnread: false),
isRemovedFromTotalUnreadCount: false,
presence: presenceTimestamp.flatMap { presenceTimestamp in
EnginePeer.Presence(status: .present(until: presenceTimestamp + 1000), lastActivity: presenceTimestamp)
},
hasUnseenMentions: false,
draftState: nil,
inputActivities: hasInputActivity ? [(author, .typingText)] : [],
promoInfo: nil,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: false
),
editing: false,
hasActiveRevealControls: false,
selected: false,
header: nil,
enableContextActions: false,
hiddenOffset: false,
interaction: interaction
)
}
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil))
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer1,
author: selfPeer,
timestamp: timestamp1,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text
))
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
let timestamp2 = timestamp + 3660
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer2), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer2,
author: peer2,
timestamp: timestamp2,
text: "",
presenceTimestamp: presenceTimestamp,
hasInputActivity: true
))
let timestamp3 = timestamp + 3200
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer3,
author: peer3Author,
timestamp: timestamp3,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text
))
let timestamp4 = timestamp + 3000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer4,
author: peer4,
timestamp: timestamp4,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text
))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let chatNodes = self.chatNodes {
@ -934,7 +1019,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
sampleMessages.append(message5)
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])

View File

@ -360,41 +360,142 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
gesture?.cancel()
}, present: { _ in
})
func makeChatListItem(
peer: EnginePeer,
author: EnginePeer,
timestamp: Int32,
text: String,
isPinned: Bool = false,
presenceTimestamp: Int32? = nil,
hasInputActivity: Bool = false,
unreadCount: Int32 = 0
) -> ChatListItem {
return ChatListItem(
presentationData: chatListPresentationData,
context: self.context,
peerGroupId: .root,
filterData: nil,
index: ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp)),
content: .peer(
messages: [
EngineMessage(
stableId: 0,
stableVersion: 0,
id: EngineMessage.Id(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0),
globallyUniqueId: nil,
groupingKey: nil,
groupInfo: nil,
threadId: nil,
timestamp: timestamp,
flags: author.id == peer.id ? [.Incoming] : [],
tags: [],
globalTags: [],
localTags: [],
forwardInfo: nil,
author: author,
text: text,
attributes: [],
media: [],
peers: [:],
associatedMessages: [:],
associatedMessageIds: []
)
],
peer: EngineRenderedPeer(peer: peer),
combinedReadState: EnginePeerReadCounters(incomingReadId: 1000, outgoingReadId: 1000, count: unreadCount, markedUnread: false),
isRemovedFromTotalUnreadCount: false,
presence: presenceTimestamp.flatMap { presenceTimestamp in
EnginePeer.Presence(status: .present(until: presenceTimestamp + 1000), lastActivity: presenceTimestamp)
},
hasUnseenMentions: false,
draftState: nil,
inputActivities: hasInputActivity ? [(author, .typingText)] : [],
promoInfo: nil,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: false
),
editing: false,
hasActiveRevealControls: false,
selected: false,
header: nil,
enableContextActions: false,
hiddenOffset: false,
interaction: interaction
)
}
let chatListPresentationData = ChatListPresentationData(theme: self.previewTheme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
let selfPeer = TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer3 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil)
let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer5 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil)
let peer6 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer7 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil))
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil))
let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
let timestamp = self.referenceTimestamp
let timestamp1 = timestamp + 120
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: 0, messageIndex: MessageIndex(id: MessageId(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: selfPeer, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer1), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer1,
author: selfPeer,
timestamp: timestamp1,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Text
))
let presenceTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + 60 * 60)
let timestamp2 = timestamp + 3660
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer2.id, namespace: 0, id: 0), timestamp: timestamp2)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp2, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer2, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer2), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: TelegramUserPresence(status: .present(until: presenceTimestamp), lastActivity: presenceTimestamp), summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: [(peer2, .typingText)], promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer2,
author: peer2,
timestamp: timestamp2,
text: "",
presenceTimestamp: presenceTimestamp,
hasInputActivity: true
))
let timestamp3 = timestamp + 3200
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer3.id, namespace: 0, id: 0), timestamp: timestamp3)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp3, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer3Author, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer3), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer3,
author: peer3Author,
timestamp: timestamp3,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Text
))
let timestamp4 = timestamp + 3000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer4), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer4,
author: peer4,
timestamp: timestamp4,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text
))
let timestamp5 = timestamp + 1000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer5.id, namespace: 0, id: 0), timestamp: timestamp5)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp5, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer5, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer5), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer5,
author: peer5,
timestamp: timestamp5,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Text
))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer6.id, namespace: 0, id: 0), timestamp: timestamp - 360)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer6.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp - 360, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer6), combinedReadState: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 1, markedUnread: false))]), isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer6,
author: peer6,
timestamp: timestamp - 360,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Text
))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer7.id, namespace: 0, id: 0), timestamp: timestamp - 420)), content: .peer(messages: [Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer7.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp - 420, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer6, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], peer: RenderedPeer(peer: peer7), combinedReadState: nil, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(makeChatListItem(
peer: peer7,
author: peer6,
timestamp: timestamp - 420,
text: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Text
))
let width: CGFloat
if case .regular = layout.metrics.widthClass {
@ -479,7 +580,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
sampleMessages.append(message5)
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])

View File

@ -243,7 +243,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased())
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> = .single(([], [], true))
|> then(
context.engine.peers.searchPeers(query: query)
context.engine.contacts.searchRemotePeers(query: query)
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
|> map { a, b -> ([FoundPeer], [FoundPeer], Bool) in
return (a, b, false)

View File

@ -250,7 +250,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
waveform = MemoryBuffer(data: waveformData)
}
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(audioData), mimeType: mimeType, attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 10 * 1024 * 1024)
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(audioData), mimeType: mimeType, attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform?.makeData()), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 10 * 1024 * 1024)
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {

View File

@ -2488,7 +2488,7 @@ public final class VoiceChatController: ViewController {
if peers.count > 1 {
for peer in peers {
if peer.peer.id == myPeerId {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAs, textLayout: .secondLineWithValue(EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer.peer, size: avatarSize)), action: { c, _ in
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAs, textLayout: .secondLineWithValue(EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize)), action: { c, _ in
guard let strongSelf = self else {
return
}
@ -2883,7 +2883,7 @@ public final class VoiceChatController: ViewController {
let isSelected = peer.peer.id == myPeerId
let extendedAvatarSize = CGSize(width: 35.0, height: 35.0)
let avatarSignal = peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer.peer, size: avatarSize)
let avatarSignal = peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize)
|> map { image -> UIImage? in
if isSelected, let image = image {
return generateImage(extendedAvatarSize, rotatedContext: { size, context in

View File

@ -732,8 +732,8 @@ final class VoiceChatMainStageNode: ASDisplayNode {
let peer = peerEntry.peer
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
if !arePeersEqual(previousPeerEntry?.peer, peerEntry.peer) {
self.backdropAvatarNode.setSignal(peerAvatarCompleteImage(account: self.context.account, peer: peer, size: CGSize(width: 240.0, height: 240.0), round: false, font: avatarPlaceholderFont(size: 78.0), drawLetters: false, blurred: true))
self.avatarNode.setSignal(peerAvatarCompleteImage(account: self.context.account, peer: peer, size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true))
self.backdropAvatarNode.setSignal(peerAvatarCompleteImage(account: self.context.account, peer: EnginePeer(peer), size: CGSize(width: 240.0, height: 240.0), round: false, font: avatarPlaceholderFont(size: 78.0), drawLetters: false, blurred: true))
self.avatarNode.setSignal(peerAvatarCompleteImage(account: self.context.account, peer: EnginePeer(peer), size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true))
}
var gradient: VoiceChatBlobNode.Gradient = .active

View File

@ -930,7 +930,7 @@ private class VoiceChatTileShimmeringNode: ASDisplayNode {
self.addSubnode(self.borderNode)
self.borderNode.addSubnode(self.borderEffectNode)
self.backgroundNode.setSignal(peerAvatarCompleteImage(account: account, peer: peer, size: CGSize(width: 250.0, height: 250.0), round: false, font: Font.regular(16.0), drawLetters: false, fullSize: false, blurred: true))
self.backgroundNode.setSignal(peerAvatarCompleteImage(account: account, peer: EnginePeer(peer), size: CGSize(width: 250.0, height: 250.0), round: false, font: Font.regular(16.0), drawLetters: false, fullSize: false, blurred: true))
}
public override func didLoad() {

View File

@ -404,6 +404,8 @@ public func managedCleanupAccounts(networkArguments: NetworkInitializationArgume
}
}
public typealias AccountManagerPreferencesEntry = PreferencesEntry
private func cleanupAccount(networkArguments: NetworkInitializationArguments, accountManager: AccountManager<TelegramAccountManagerTypes>, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, attributes: [TelegramAccountManagerTypes.Attribute], rootPath: String, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal<Void, NoError> {
let beginWithTestingEnvironment = attributes.contains(where: { attribute in
if case let .environment(accountEnvironment) = attribute, case .test = accountEnvironment.environment {

View File

@ -1,4 +1,5 @@
import Foundation
import Postbox
final class MutableAccessChallengeDataView {
var data: PostboxAccessChallengeData

View File

@ -1,5 +1,6 @@
import Foundation
import SwiftSignalKit
import Postbox
public protocol AccountManagerTypes {
associatedtype Attribute: AccountRecordAttribute

View File

@ -1,4 +1,5 @@
import Foundation
import Postbox
public struct AccessChallengeAttempts: Equatable {
public let count: Int32

View File

@ -1,4 +1,5 @@
import Foundation
import Postbox
enum AccountManagerRecordOperation<Attribute: AccountRecordAttribute> {
case set(id: AccountRecordId, record: AccountRecord<Attribute>?)

View File

@ -1,4 +1,5 @@
import Foundation
import Postbox
final class AccountManagerSharedDataTable: Table {

View File

@ -1,4 +1,5 @@
import Foundation
import Postbox
public protocol AccountRecordAttribute: Codable {
func isEqual(to: AccountRecordAttribute) -> Bool

View File

@ -1,4 +1,5 @@
import Foundation
import Postbox
final class MutableAccountSharedDataView<Types: AccountManagerTypes> {
private let keys: Set<ValueBoxKey>

View File

@ -1,4 +1,5 @@
import Foundation
import Postbox
final class MutableNoticeEntryView<Types: AccountManagerTypes> {
private let key: NoticeEntryKey

View File

@ -100,12 +100,7 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
result.append(.Video(duration: Int(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags))
case let .documentAttributeAudio(flags, duration, title, performer, waveform):
let isVoice = (flags & (1 << 10)) != 0
var waveformBuffer: MemoryBuffer?
if let waveform = waveform {
let memory = malloc(waveform.size)!
memcpy(memory, waveform.data, waveform.size)
waveformBuffer = MemoryBuffer(memory: memory, capacity: waveform.size, length: waveform.size, freeWhenDone: true)
}
let waveformBuffer: Data? = waveform?.makeData()
result.append(.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer))
}
}

View File

@ -539,7 +539,7 @@ func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaF
var waveformBuffer: Buffer?
if let waveform = waveform {
flags |= Int32(1 << 2)
waveformBuffer = Buffer(data: waveform.makeData())
waveformBuffer = Buffer(data: waveform)
}
attributes.append(.documentAttributeAudio(flags: flags, duration: Int32(duration), title: title, performer: performer, waveform: waveformBuffer))
}

View File

@ -517,7 +517,7 @@ private func decryptedAttributes46(_ attributes: [TelegramMediaFileAttribute], t
var waveformBuffer: Buffer?
if let waveform = waveform {
flags |= Int32(1 << 2)
waveformBuffer = Buffer(data: waveform.makeData())
waveformBuffer = Buffer(data: waveform)
}
result.append(.documentAttributeAudio(flags: flags, duration: Int32(duration), title: title, performer: performer, waveform: waveformBuffer))
case .HasLinkedStickers:
@ -576,7 +576,7 @@ private func decryptedAttributes73(_ attributes: [TelegramMediaFileAttribute], t
var waveformBuffer: Buffer?
if let waveform = waveform {
flags |= Int32(1 << 2)
waveformBuffer = Buffer(data: waveform.makeData())
waveformBuffer = Buffer(data: waveform)
}
result.append(.documentAttributeAudio(flags: flags, duration: Int32(duration), title: title, performer: performer, waveform: waveformBuffer))
case .HasLinkedStickers:
@ -635,7 +635,7 @@ private func decryptedAttributes101(_ attributes: [TelegramMediaFileAttribute],
var waveformBuffer: Buffer?
if let waveform = waveform {
flags |= Int32(1 << 2)
waveformBuffer = Buffer(data: waveform.makeData())
waveformBuffer = Buffer(data: waveform)
}
result.append(.documentAttributeAudio(flags: flags, duration: Int32(duration), title: title, performer: performer, waveform: waveformBuffer))
case .HasLinkedStickers:

View File

@ -538,12 +538,7 @@ extension TelegramMediaFileAttribute {
self = .Animated
case let .documentAttributeAudio(flags, duration, title, performer, waveform):
let isVoice = (flags & (1 << 10)) != 0
var waveformBuffer: MemoryBuffer?
if let waveform = waveform {
let memory = malloc(waveform.size)!
memcpy(memory, waveform.data, waveform.size)
waveformBuffer = MemoryBuffer(memory: memory, capacity: waveform.size, length: waveform.size, freeWhenDone: true)
}
let waveformBuffer: Data? = waveform?.makeData()
self = .Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer)
case let .documentAttributeFilename(fileName):
self = .FileName(fileName: fileName)
@ -571,12 +566,7 @@ extension TelegramMediaFileAttribute {
self = .Animated
case let .documentAttributeAudio(flags, duration, title, performer, waveform):
let isVoice = (flags & (1 << 10)) != 0
var waveformBuffer: MemoryBuffer?
if let waveform = waveform {
let memory = malloc(waveform.size)!
memcpy(memory, waveform.data, waveform.size)
waveformBuffer = MemoryBuffer(memory: memory, capacity: waveform.size, length: waveform.size, freeWhenDone: true)
}
let waveformBuffer: Data? = waveform?.makeData()
self = .Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer)
case let .documentAttributeFilename(fileName):
self = .FileName(fileName: fileName)
@ -608,12 +598,7 @@ extension TelegramMediaFileAttribute {
self = .Animated
case let .documentAttributeAudio(flags, duration, title, performer, waveform):
let isVoice = (flags & (1 << 10)) != 0
var waveformBuffer: MemoryBuffer?
if let waveform = waveform {
let memory = malloc(waveform.size)!
memcpy(memory, waveform.data, waveform.size)
waveformBuffer = MemoryBuffer(memory: memory, capacity: waveform.size, length: waveform.size, freeWhenDone: true)
}
let waveformBuffer: Data? = waveform?.makeData()
self = .Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer)
case let .documentAttributeFilename(fileName):
self = .FileName(fileName: fileName)

View File

@ -140,7 +140,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
case ImageSize(size: PixelDimensions)
case Animated
case Video(duration: Int, size: PixelDimensions, flags: TelegramMediaVideoFlags)
case Audio(isVoice: Bool, duration: Int, title: String?, performer: String?, waveform: MemoryBuffer?)
case Audio(isVoice: Bool, duration: Int, title: String?, performer: String?, waveform: Data?)
case HasLinkedStickers
case hintFileIsLarge
case hintIsValidated
@ -160,9 +160,9 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
self = .Video(duration: Int(decoder.decodeInt32ForKey("du", orElse: 0)), size: PixelDimensions(width: decoder.decodeInt32ForKey("w", orElse: 0), height: decoder.decodeInt32ForKey("h", orElse: 0)), flags: TelegramMediaVideoFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0)))
case typeAudio:
let waveformBuffer = decoder.decodeBytesForKeyNoCopy("wf")
var waveform: MemoryBuffer?
var waveform: Data?
if let waveformBuffer = waveformBuffer {
waveform = MemoryBuffer(copyOf: waveformBuffer)
waveform = waveformBuffer.makeData()
}
self = .Audio(isVoice: decoder.decodeInt32ForKey("iv", orElse: 0) != 0, duration: Int(decoder.decodeInt32ForKey("du", orElse: 0)), title: decoder.decodeOptionalStringForKey("ti"), performer: decoder.decodeOptionalStringForKey("pe"), waveform: waveform)
case typeHasLinkedStickers:
@ -217,7 +217,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
encoder.encodeString(performer, forKey: "pe")
}
if let waveform = waveform {
encoder.encodeBytes(waveform, forKey: "wf")
encoder.encodeBytes(MemoryBuffer(data: waveform), forKey: "wf")
}
case .HasLinkedStickers:
encoder.encodeInt32(typeHasLinkedStickers, forKey: "t")

View File

@ -40,5 +40,16 @@ public extension TelegramEngine {
public func acceptAndShareContact(peerId: PeerId) -> Signal<Never, AcceptAndShareContactError> {
return _internal_acceptAndShareContact(account: self.account, peerId: peerId)
}
public func searchRemotePeers(query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> {
return _internal_searchPeers(account: self.account, query: query)
}
public func searchLocalPeers(query: String) -> Signal<[EngineRenderedPeer], NoError> {
return self.account.postbox.searchPeers(query: query)
|> map { peers in
return peers.map(EngineRenderedPeer.init)
}
}
}
}

View File

@ -13,6 +13,45 @@ public final class EngineTotalReadCounters {
}
}
public struct EnginePeerReadCounters: Equatable {
private let state: CombinedPeerReadState
public init(state: CombinedPeerReadState) {
self.state = state
}
public init() {
self.state = CombinedPeerReadState(states: [])
}
public var count: Int32 {
return self.state.count
}
public var markedUnread: Bool {
return self.state.markedUnread
}
public var isUnread: Bool {
return self.state.isUnread
}
public func isOutgoingMessageIndexRead(_ index: EngineMessage.Index) -> Bool {
return self.state.isOutgoingMessageIndexRead(index)
}
public func isIncomingMessageIndexRead(_ index: EngineMessage.Index) -> Bool {
return self.state.isIncomingMessageIndexRead(index)
}
}
public extension EnginePeerReadCounters {
init(incomingReadId: EngineMessage.Id.Id, outgoingReadId: EngineMessage.Id.Id, count: Int32, markedUnread: Bool) {
self.init(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: incomingReadId, maxOutgoingReadId: outgoingReadId, maxKnownId: max(incomingReadId, outgoingReadId), count: count, markedUnread: markedUnread))]))
}
}
public extension TelegramEngine.EngineData.Item {
enum Messages {
public struct Message: TelegramEngineDataItem, PostboxViewDataItem {
@ -64,6 +103,31 @@ public extension TelegramEngine.EngineData.Item {
}
}
public struct PeerUnreadCount: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Int
fileprivate let id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
var key: PostboxViewKey {
return .unreadCounts(items: [.peer(self.id)])
}
public init(id: EnginePeer.Id) {
self.id = id
}
func extract(view: PostboxView) -> Int {
guard let view = view as? UnreadMessageCountsView else {
preconditionFailure()
}
return Int(view.count(for: .peer(self.id)) ?? 0)
}
}
public struct TotalReadCounters: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = EngineTotalReadCounters

View File

@ -3,10 +3,13 @@ import Postbox
public extension TelegramEngine.EngineData.Item {
enum Peer {
public struct Peer: TelegramEngineDataItem, PostboxViewDataItem {
public struct Peer: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Optional<EnginePeer>
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
@ -27,10 +30,108 @@ public extension TelegramEngine.EngineData.Item {
}
}
public struct RenderedPeer: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = Optional<EngineRenderedPeer>
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
}
var key: PostboxViewKey {
return .peer(peerId: self.id, components: [])
}
func extract(view: PostboxView) -> Result {
guard let view = view as? PeerView else {
preconditionFailure()
}
var peers: [EnginePeer.Id: EnginePeer] = [:]
guard let peer = view.peers[self.id] else {
return nil
}
peers[peer.id] = EnginePeer(peer)
if let secretChat = peer as? TelegramSecretChat {
guard let mainPeer = view.peers[secretChat.regularPeerId] else {
return nil
}
peers[mainPeer.id] = EnginePeer(mainPeer)
}
return EngineRenderedPeer(peerId: self.id, peers: peers)
}
}
public struct Presence: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = Optional<EnginePeer.Presence>
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
}
var key: PostboxViewKey {
return .peer(peerId: self.id, components: [])
}
func extract(view: PostboxView) -> Result {
guard let view = view as? PeerView else {
preconditionFailure()
}
var presencePeerId = self.id
if let secretChat = view.peers[self.id] as? TelegramSecretChat {
presencePeerId = secretChat.regularPeerId
}
guard let presence = view.peerPresences[presencePeerId] else {
return nil
}
return EnginePeer.Presence(presence)
}
}
public struct NotificationSettings: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Optional<EnginePeer.NotificationSettings>
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
}
var key: PostboxViewKey {
return .peer(peerId: self.id, components: [])
}
func extract(view: PostboxView) -> Result {
guard let view = view as? PeerView else {
preconditionFailure()
}
guard let notificationSettings = view.notificationSettings as? TelegramPeerNotificationSettings else {
return nil
}
return EnginePeer.NotificationSettings(notificationSettings)
}
}
public struct ParticipantCount: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = Optional<Int>
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
@ -62,6 +163,9 @@ public extension TelegramEngine.EngineData.Item {
public typealias Result = Optional<EngineGroupCallDescription>
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id

View File

@ -5,19 +5,62 @@ public protocol TelegramEngineDataItem {
associatedtype Result
}
protocol AnyPostboxViewDataItem {
var key: PostboxViewKey { get }
public protocol TelegramEngineMapKeyDataItem {
associatedtype Key: Hashable
func _extract(view: PostboxView) -> Any
var mapKey: Key { get }
}
protocol AnyPostboxViewDataItem {
var keys: [PostboxViewKey] { get }
func _extract(views: [PostboxViewKey: PostboxView]) -> Any
}
protocol PostboxViewDataItem: TelegramEngineDataItem, AnyPostboxViewDataItem {
var key: PostboxViewKey { get }
func extract(view: PostboxView) -> Result
}
extension PostboxViewDataItem {
func _extract(view: PostboxView) -> Any {
return self.extract(view: view)
var keys: [PostboxViewKey] {
return [self.key]
}
func _extract(views: [PostboxViewKey: PostboxView]) -> Any {
return self.extract(view: views[self.key]!)
}
}
public final class EngineDataMap<Item: TelegramEngineDataItem & TelegramEngineMapKeyDataItem>: TelegramEngineDataItem, AnyPostboxViewDataItem {
public typealias Result = [Item.Key: Item.Result]
private let items: [Item]
public init(_ items: [Item]) {
self.items = items
}
var keys: [PostboxViewKey] {
var keys = Set<PostboxViewKey>()
for item in self.items {
for key in (item as! AnyPostboxViewDataItem).keys {
keys.insert(key)
}
}
return Array(keys)
}
func _extract(views: [PostboxViewKey: PostboxView]) -> Any {
var result: [Item.Key: Item.Result] = [:]
for item in self.items {
let itemResult = (item as! AnyPostboxViewDataItem)._extract(views: views)
result[item.mapKey] = (itemResult as! Item.Result)
}
return result
}
}
@ -33,15 +76,18 @@ public extension TelegramEngine {
}
private func _subscribe(items: [AnyPostboxViewDataItem]) -> Signal<[Any], NoError> {
return self.account.postbox.combinedView(keys: Array(Set(items.map(\.key))))
var keys = Set<PostboxViewKey>()
for item in items {
for key in item.keys {
keys.insert(key)
}
}
return self.account.postbox.combinedView(keys: Array(keys))
|> map { views -> [Any] in
var results: [Any] = []
for item in items {
guard let view = views.views[item.key] else {
preconditionFailure()
}
results.append(item._extract(view: view))
results.append(item._extract(views: views.views))
}
return results

View File

@ -6,12 +6,23 @@ public final class EngineChatList {
case archive
}
public typealias MessageTagSummaryInfo = ChatListMessageTagSummaryInfo
public enum PinnedItem {
public typealias Id = PinnedItemId
}
public enum RelativePosition {
case later(than: EngineChatList.Item.Index?)
case earlier(than: EngineChatList.Item.Index?)
}
public final class Item {
public typealias Index = ChatListIndex
public let index: Index
public let messages: [EngineMessage]
public let readState: EngineReadState?
public let readCounters: EnginePeerReadCounters?
public let isMuted: Bool
public let draftText: String?
public let renderedPeer: EngineRenderedPeer
@ -23,7 +34,7 @@ public final class EngineChatList {
public init(
index: Index,
messages: [EngineMessage],
readState: EngineReadState?,
readCounters: EnginePeerReadCounters?,
isMuted: Bool,
draftText: String?,
renderedPeer: EngineRenderedPeer,
@ -34,7 +45,7 @@ public final class EngineChatList {
) {
self.index = index
self.messages = messages
self.readState = readState
self.readCounters = readCounters
self.isMuted = isMuted
self.draftText = draftText
self.renderedPeer = renderedPeer
@ -45,8 +56,8 @@ public final class EngineChatList {
}
}
public final class GroupItem {
public final class Item {
public final class GroupItem: Equatable {
public final class Item: Equatable {
public let peer: EngineRenderedPeer
public let isUnread: Bool
@ -54,6 +65,16 @@ public final class EngineChatList {
self.peer = peer
self.isUnread = isUnread
}
public static func ==(lhs: Item, rhs: Item) -> Bool {
if lhs.peer != rhs.peer {
return false
}
if lhs.isUnread != rhs.isUnread {
return false
}
return true
}
}
public let id: Group
@ -72,23 +93,71 @@ public final class EngineChatList {
self.items = items
self.unreadCount = unreadCount
}
public static func ==(lhs: GroupItem, rhs: GroupItem) -> Bool {
if lhs.id != rhs.id {
return false
}
if lhs.topMessage?.index != rhs.topMessage?.index {
return false
}
if lhs.topMessage?.stableVersion != rhs.topMessage?.stableVersion {
return false
}
if lhs.items != rhs.items {
return false
}
if lhs.unreadCount != rhs.unreadCount {
return false
}
return true
}
}
public final class AdditionalItem {
public final class PromoInfo {
public enum Content {
case proxy
case psa(type: String, message: String?)
}
public let content: Content
public init(content: Content) {
self.content = content
}
}
public let item: Item
public let promoInfo: PromoInfo
public init(item: Item, promoInfo: PromoInfo) {
self.item = item
self.promoInfo = promoInfo
}
}
public let items: [Item]
public let groupItems: [GroupItem]
public let additionalItems: [AdditionalItem]
public let hasEarlier: Bool
public let hasLater: Bool
public let isLoading: Bool
init(
items: [Item],
groupItems: [GroupItem],
additionalItems: [AdditionalItem],
hasEarlier: Bool,
hasLater: Bool
hasLater: Bool,
isLoading: Bool
) {
self.items = items
self.groupItems = groupItems
self.additionalItems = additionalItems
self.hasEarlier = hasEarlier
self.hasLater = hasLater
self.isLoading = isLoading
}
}
@ -113,16 +182,44 @@ public extension EngineChatList.Group {
}
}
public extension EngineChatList.RelativePosition {
init(_ position: ChatListRelativePosition) {
switch position {
case let .earlier(than):
self = .earlier(than: than)
case let .later(than):
self = .later(than: than)
}
}
func _asPosition() -> ChatListRelativePosition {
switch self {
case let .earlier(than):
return .earlier(than: than)
case let .later(than):
return .later(than: than)
}
}
}
extension EngineChatList.Item {
convenience init?(_ entry: ChatListEntry) {
switch entry {
case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, _, renderedPeer, presence, summaryInfo, hasFailed, isContact):
case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedState, renderedPeer, presence, summaryInfo, hasFailed, isContact):
var draftText: String?
if let embeddedState = embeddedState, let _ = embeddedState.overrideChatTimestamp {
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
if let text = opaqueState.synchronizeableInputState?.text {
draftText = text
}
}
}
self.init(
index: index,
messages: messages.map(EngineMessage.init),
readState: readState.flatMap(EngineReadState.init),
readCounters: readState.flatMap(EnginePeerReadCounters.init),
isMuted: isRemovedFromTotalUnreadCount,
draftText: nil,
draftText: draftText,
renderedPeer: EngineRenderedPeer(renderedPeer),
presence: presence.flatMap(EnginePeer.Presence.init),
hasUnseenMentions: (summaryInfo.tagSummaryCount ?? 0) > (summaryInfo.actionsSummaryCount ?? 0),
@ -151,13 +248,56 @@ extension EngineChatList.GroupItem {
}
}
extension EngineChatList {
extension EngineChatList.AdditionalItem.PromoInfo {
convenience init(_ item: PromoChatListItem) {
let content: EngineChatList.AdditionalItem.PromoInfo.Content
switch item.kind {
case .proxy:
content = .proxy
case let .psa(type, message):
content = .psa(type: type, message: message)
}
self.init(content: content)
}
}
extension EngineChatList.AdditionalItem {
convenience init?(_ entry: ChatListAdditionalItemEntry) {
guard let item = EngineChatList.Item(entry.entry) else {
return nil
}
guard let promoInfo = (entry.info as? PromoChatListItem).flatMap(EngineChatList.AdditionalItem.PromoInfo.init) else {
return nil
}
self.init(item: item, promoInfo: promoInfo)
}
}
public extension EngineChatList {
convenience init(_ view: ChatListView) {
var isLoading = false
var items: [EngineChatList.Item] = []
loop: for entry in view.entries {
switch entry {
case .MessageEntry:
if let item = EngineChatList.Item(entry) {
items.append(item)
}
case .HoleEntry:
isLoading = true
break loop
}
}
self.init(
items: view.entries.compactMap(EngineChatList.Item.init),
items: items,
groupItems: view.groupEntries.map(EngineChatList.GroupItem.init),
additionalItems: view.additionalItemEntries.compactMap(EngineChatList.AdditionalItem.init),
hasEarlier: view.earlierIndex != nil,
hasLater: view.laterIndex != nil
hasLater: view.laterIndex != nil,
isLoading: isLoading
)
}
}

View File

@ -30,6 +30,39 @@ public enum EngineMedia {
case webpage(TelegramMediaWebpage)
}
public extension EngineMedia {
var id: Id? {
switch self {
case let .image(image):
return image.id
case let .file(file):
return file.id
case let .geo(geo):
return geo.id
case let .contact(contact):
return contact.id
case let .action(action):
return action.id
case let .dice(dice):
return dice.id
case let .expiredContent(expiredContent):
return expiredContent.id
case let .game(game):
return game.id
case let .invoice(invoice):
return invoice.id
case let .poll(poll):
return poll.id
case let .unsupported(unsupported):
return unsupported.id
case let .webFile(webFile):
return webFile.id
case let .webpage(webpage):
return webpage.id
}
}
}
public extension EngineMedia {
init(_ media: Media) {
switch media {

View File

@ -3,6 +3,13 @@ import Postbox
public final class EngineMessage {
public typealias Id = MessageId
public typealias Index = MessageIndex
public typealias Tags = MessageTags
public typealias Attribute = MessageAttribute
public typealias GroupInfo = MessageGroupInfo
public typealias Flags = MessageFlags
public typealias GlobalTags = GlobalMessageTags
public typealias LocalTags = LocalMessageTags
public typealias ForwardInfo = MessageForwardInfo
private let impl: Message
@ -23,7 +30,7 @@ public final class EngineMessage {
public var groupingKey: Int64? {
return self.impl.groupingKey
}
public var groupInfo: MessageGroupInfo? {
public var groupInfo: GroupInfo? {
return self.impl.groupInfo
}
public var threadId: Int64? {
@ -32,19 +39,19 @@ public final class EngineMessage {
public var timestamp: Int32 {
return self.impl.timestamp
}
public var flags: MessageFlags {
public var flags: Flags {
return self.impl.flags
}
public var tags: MessageTags {
public var tags: Tags {
return self.impl.tags
}
public var globalTags: GlobalMessageTags {
public var globalTags: GlobalTags {
return self.impl.globalTags
}
public var localTags: LocalMessageTags {
public var localTags: LocalTags {
return self.impl.localTags
}
public var forwardInfo: MessageForwardInfo? {
public var forwardInfo: ForwardInfo? {
return self.impl.forwardInfo
}
public var author: EnginePeer? {
@ -53,19 +60,19 @@ public final class EngineMessage {
public var text: String {
return self.impl.text
}
public var attributes: [MessageAttribute] {
public var attributes: [Attribute] {
return self.impl.attributes
}
public var media: [Media] {
return self.impl.media
}
public var peers: SimpleDictionary<PeerId, Peer> {
public var peers: SimpleDictionary<EnginePeer.Id, Peer> {
return self.impl.peers
}
public var associatedMessages: SimpleDictionary<MessageId, Message> {
public var associatedMessages: SimpleDictionary<EngineMessage.Id, Message> {
return self.impl.associatedMessages
}
public var associatedMessageIds: [MessageId] {
public var associatedMessageIds: [EngineMessage.Id] {
return self.impl.associatedMessageIds
}
@ -73,6 +80,62 @@ public final class EngineMessage {
return self.impl.index
}
public init(
stableId: UInt32,
stableVersion: UInt32,
id: EngineMessage.Id,
globallyUniqueId: Int64?,
groupingKey: Int64?,
groupInfo: EngineMessage.GroupInfo?,
threadId: Int64?,
timestamp: Int32,
flags: EngineMessage.Flags,
tags: EngineMessage.Tags,
globalTags: EngineMessage.GlobalTags,
localTags: EngineMessage.LocalTags,
forwardInfo: EngineMessage.ForwardInfo?,
author: EnginePeer?,
text: String,
attributes: [Attribute],
media: [EngineMedia],
peers: [EnginePeer.Id: EnginePeer],
associatedMessages: [EngineMessage.Id: EngineMessage],
associatedMessageIds: [EngineMessage.Id]
) {
var mappedPeers: [PeerId: Peer] = [:]
for (id, peer) in peers {
mappedPeers[id] = peer._asPeer()
}
var mappedAssociatedMessages: [MessageId: Message] = [:]
for (id, message) in associatedMessages {
mappedAssociatedMessages[id] = message._asMessage()
}
self.impl = Message(
stableId: stableId,
stableVersion: stableVersion,
id: id,
globallyUniqueId: globallyUniqueId,
groupingKey: groupingKey,
groupInfo: groupInfo,
threadId: threadId,
timestamp: timestamp,
flags: flags,
tags: tags,
globalTags: globalTags,
localTags: localTags,
forwardInfo: forwardInfo,
author: author?._asPeer(),
text: text,
attributes: attributes,
media: media.map { $0._asMedia() },
peers: SimpleDictionary(mappedPeers),
associatedMessages: SimpleDictionary(mappedAssociatedMessages),
associatedMessageIds: associatedMessageIds
)
}
public init(_ impl: Message) {
self.impl = impl
}

View File

@ -1,23 +0,0 @@
import Postbox
public struct EngineReadState: Equatable {
public var unreadCount: Int
public var isMarkedAsUnread: Bool
public init(unreadCount: Int, isMarkedAsUnread: Bool) {
self.unreadCount = unreadCount
self.isMarkedAsUnread = isMarkedAsUnread
}
}
public extension EngineReadState {
var isUnread: Bool {
return self.unreadCount != 0 || self.isMarkedAsUnread
}
}
public extension EngineReadState {
init(_ readState: CombinedPeerReadState) {
self.init(unreadCount: Int(readState.count), isMarkedAsUnread: readState.markedUnread)
}
}

View File

@ -21,6 +21,41 @@ public enum EnginePeer: Equatable {
}
}
public struct NotificationSettings: Equatable {
public enum MuteState: Equatable {
case `default`
case unmuted
case muted(until: Int32)
}
public enum MessageSound: Equatable {
case none
case `default`
case bundledModern(id: Int32)
case bundledClassic(id: Int32)
}
public enum DisplayPreviews {
case `default`
case show
case hide
}
public var muteState: MuteState
public var messageSound: MessageSound
public var displayPreviews: DisplayPreviews
public init(
muteState: MuteState,
messageSound: MessageSound,
displayPreviews: DisplayPreviews
) {
self.muteState = muteState
self.messageSound = messageSound
self.displayPreviews = displayPreviews
}
}
case user(TelegramUser)
case legacyGroup(TelegramGroup)
case channel(TelegramChannel)
@ -56,6 +91,100 @@ public enum EnginePeer: Equatable {
}
}
public extension EnginePeer.NotificationSettings.MuteState {
init(_ muteState: PeerMuteState) {
switch muteState {
case .default:
self = .default
case .unmuted:
self = .unmuted
case let .muted(until):
self = .muted(until: until)
}
}
func _asMuteState() -> PeerMuteState {
switch self {
case .default:
return .default
case .unmuted:
return .unmuted
case let .muted(until):
return .muted(until: until)
}
}
}
public extension EnginePeer.NotificationSettings.MessageSound {
init(_ messageSound: PeerMessageSound) {
switch messageSound {
case .none:
self = .none
case .default:
self = .default
case let .bundledClassic(id):
self = .bundledClassic(id: id)
case let .bundledModern(id):
self = .bundledModern(id: id)
}
}
func _asMessageSound() -> PeerMessageSound {
switch self {
case .none:
return .none
case .default:
return .default
case let .bundledClassic(id):
return .bundledClassic(id: id)
case let .bundledModern(id):
return .bundledModern(id: id)
}
}
}
public extension EnginePeer.NotificationSettings.DisplayPreviews {
init(_ displayPreviews: PeerNotificationDisplayPreviews) {
switch displayPreviews {
case .default:
self = .default
case .show:
self = .show
case .hide:
self = .hide
}
}
func _asDisplayPreviews() -> PeerNotificationDisplayPreviews {
switch self {
case .default:
return .default
case .show:
return .show
case .hide:
return .hide
}
}
}
public extension EnginePeer.NotificationSettings {
init(_ notificationSettings: TelegramPeerNotificationSettings) {
self.init(
muteState: MuteState(notificationSettings.muteState),
messageSound: MessageSound(notificationSettings.messageSound),
displayPreviews: DisplayPreviews(notificationSettings.displayPreviews)
)
}
func _asNotificationSettings() -> TelegramPeerNotificationSettings {
return TelegramPeerNotificationSettings(
muteState: self.muteState._asMuteState(),
messageSound: self.messageSound._asMessageSound(),
displayPreviews: self.displayPreviews._asDisplayPreviews()
)
}
}
public extension EnginePeer.Presence {
init(_ presence: PeerPresence) {
if let presence = presence as? TelegramUserPresence {
@ -174,7 +303,7 @@ public extension EnginePeer {
}
}
public final class EngineRenderedPeer {
public final class EngineRenderedPeer: Equatable {
public let peerId: EnginePeer.Id
public let peers: [EnginePeer.Id: EnginePeer]
@ -223,4 +352,8 @@ public extension EngineRenderedPeer {
}
self.init(peerId: renderedPeer.peerId, peers: mappedPeers)
}
convenience init(message: EngineMessage) {
self.init(RenderedPeer(message: message._asMessage()))
}
}

View File

@ -98,10 +98,6 @@ public extension TelegramEngine {
}
}
public func searchPeers(query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> {
return _internal_searchPeers(account: self.account, query: query)
}
public func updatedRemotePeer(peer: PeerReference) -> Signal<Peer, UpdatedRemotePeerError> {
return _internal_updatedRemotePeer(postbox: self.account.postbox, network: self.account.network, peer: peer)
}

View File

@ -231,7 +231,6 @@ swift_library(
"//Telegram:GeneratedSources",
"//third-party/ZipArchive:ZipArchive",
"//submodules/ChatImportUI:ChatImportUI",
"//submodules/ChatHistoryImportTasks:ChatHistoryImportTasks",
"//submodules/DatePickerNode:DatePickerNode",
"//submodules/ConfettiEffect:ConfettiEffect",
"//submodules/Speak:Speak",

View File

@ -57,7 +57,6 @@ import MediaResources
import GalleryData
import ChatInterfaceState
import InviteLinksUI
import ChatHistoryImportTasks
import Markdown
import TelegramPermissionsUI
import Speak
@ -1868,7 +1867,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = (peerSignal
|> deliverOnMainQueue).start(next: { peer in
if let strongSelf = self {
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer, query: hashtag)
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag)
strongSelf.effectiveNavigationController?.pushViewController(searchController)
}
})
@ -4083,22 +4082,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatTitleView?.networkState = state
}
})
/*if case let .peer(peerId) = self.chatLocation {
self.importStateDisposable = (ChatHistoryImportTasks.importState(peerId: peerId)
|> distinctUntilChanged
|> deliverOnMainQueue).start(next: { [weak self] state in
guard let strongSelf = self else {
return
}
let mappedState = state.flatMap { state -> ChatPresentationImportState in
ChatPresentationImportState(progress: state)
}
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
$0.updatedImportState(mappedState)
})
})
}*/
}
required public init(coder aDecoder: NSCoder) {
@ -10770,10 +10753,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let resource = LocalFileMediaResource(fileId: randomId)
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData)
var waveformBuffer: MemoryBuffer?
if let waveform = data.waveform {
waveformBuffer = MemoryBuffer(data: waveform)
}
let waveformBuffer: Data? = data.waveform
let correlationId = Int64.random(in: 0 ..< Int64.max)
var usedCorrelationId = false
@ -10882,7 +10862,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
let waveformBuffer = MemoryBuffer(data: recordedMediaPreview.waveform.makeBitstream())
let waveformBuffer = recordedMediaPreview.waveform.makeBitstream()
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
if let strongSelf = self {
@ -12042,7 +12022,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
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, query: hashtag)
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag)
strongSelf.effectiveNavigationController?.pushViewController(searchController)
}
}))

View File

@ -1199,7 +1199,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
subActions.append(.separator)
for peer in stats.peers {
let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer._asPeer(), size: CGSize(width: 30.0, height: 30.0))
let avatarSignal = peerAvatarCompleteImage(account: context.account, peer: peer, size: CGSize(width: 30.0, height: 30.0))
subActions.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: CGSize(width: 30.0, height: 30.0), signal: avatarSignal), action: { _, f in
c.dismiss(completion: {

View File

@ -365,9 +365,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let durationString = stringForDuration(audioDuration)
candidateDescriptionString = NSAttributedString(string: durationString, font: durationFont, textColor: messageTheme.fileDurationColor)
if let waveform = waveform {
waveform.withDataNoCopy { data in
audioWaveform = AudioWaveform(bitstream: data, bitsPerSample: 5)
}
audioWaveform = AudioWaveform(bitstream: waveform, bitsPerSample: 5)
}
} else {
candidateTitleString = NSAttributedString(string: title ?? (file.fileName ?? "Unknown Track"), font: titleFont, textColor: messageTheme.fileTitleColor)

View File

@ -206,7 +206,7 @@ final class ChatMessageAccessibilityData {
if let chatPeer = message.peers[item.message.id.peerId] {
let authorName = message.author.flatMap(EnginePeer.init)?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
let (_, _, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: [message], chatPeer: RenderedPeer(peer: chatPeer), accountPeerId: item.context.account.peerId)
let (_, _, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: [EngineMessage(message)], chatPeer: EngineRenderedPeer(peer: EnginePeer(chatPeer)), accountPeerId: item.context.account.peerId)
var text = messageText

View File

@ -288,7 +288,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
strongSelf.resolvePeerByNameDisposable.set((resolveSignal
|> deliverOnMainQueue).start(next: { peer in
if let strongSelf = self, !hashtag.isEmpty {
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer, query: hashtag)
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag)
strongSelf.pushController(searchController)
}
}))
@ -437,7 +437,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
if let strongSelf = self {
let searchController = HashtagSearchController(context: strongSelf.context, peer: strongSelf.peer, query: hashtag)
let searchController = HashtagSearchController(context: strongSelf.context, peer: EnginePeer(strongSelf.peer), query: hashtag)
strongSelf.pushController(searchController)
}
}),

View File

@ -77,7 +77,34 @@ private enum ChatListSearchEntry: Comparable, Identifiable {
public func item(context: AccountContext, interaction: ChatListNodeInteraction) -> ListViewItem {
switch self {
case let .message(message, peer, readState, presentationData):
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: true, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
return ChatListItem(
presentationData: presentationData,
context: context,
peerGroupId: .root,
filterData: nil,
index: EngineChatList.Item.Index(pinningIndex: nil, messageIndex: message.index),
content: .peer(
messages: [EngineMessage(message)],
peer: EngineRenderedPeer(peer),
combinedReadState: readState.flatMap(EnginePeerReadCounters.init),
isRemovedFromTotalUnreadCount: false,
presence: nil,
hasUnseenMentions: false,
draftState: nil,
inputActivities: nil,
promoInfo: nil,
ignoreUnreadBadge: true,
displayAsMessage: true,
hasFailedMessages: false
),
editing: false,
hasActiveRevealControls: false,
selected: false,
header: nil,
enableContextActions: false,
hiddenOffset: false,
interaction: interaction
)
}
}
}

View File

@ -129,7 +129,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
}
case let .chats(chatsNode):
chatsNode.peerSelected = { [weak self] peer, _, _, _ in
self?.openPeer?(.peer(peer: peer, isGlobal: false, participantCount: nil))
self?.openPeer?(.peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil))
}
chatsNode.additionalCategorySelected = { [weak self] id in
guard let strongSelf = self else {

View File

@ -3330,7 +3330,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
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, query: hashtag)
let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag)
strongSelf.controller?.push(searchController)
}
}))
@ -4388,7 +4388,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
if let peer = selectedPeer {
let avatarSize = CGSize(width: 28.0, height: 28.0)
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAs, textLayout: .secondLineWithValue(EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer.peer, size: avatarSize)), action: { c, f in
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAs, textLayout: .secondLineWithValue(EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize)), action: { c, f in
guard let strongSelf = self else {
return
}
@ -4500,7 +4500,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
let avatarSize = CGSize(width: 28.0, height: 28.0)
let avatarSignal = peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer.peer, size: avatarSize)
let avatarSignal = peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize)
items.append(.action(ContextMenuActionItem(text: EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), textLayout: subtitle.flatMap { .secondLineWithValue($0) } ?? .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: avatarSignal), action: { _, f in
if dismissOnSelection {
f(.dismissWithoutContent)
@ -6885,7 +6885,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
let avatarSize = CGSize(width: 28.0, height: 28.0)
items.append(.action(ContextMenuActionItem(text: primary.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: primary.0.account, peer: primary.1._asPeer(), size: avatarSize)), action: { _, f in
items.append(.action(ContextMenuActionItem(text: primary.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: primary.0.account, peer: primary.1, size: avatarSize)), action: { _, f in
f(.default)
})))
@ -6895,7 +6895,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
for account in other {
let id = account.0.account.id
items.append(.action(ContextMenuActionItem(text: account.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), badge: account.2 != 0 ? ContextMenuActionBadge(value: "\(account.2)", color: .accent) : nil, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: account.0.account, peer: account.1._asPeer(), size: avatarSize)), action: { [weak self] _, f in
items.append(.action(ContextMenuActionItem(text: account.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), badge: account.2 != 0 ? ContextMenuActionBadge(value: "\(account.2)", color: .accent) : nil, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: account.0.account, peer: account.1, size: avatarSize)), action: { [weak self] _, f in
guard let strongSelf = self else {
return
}

View File

@ -141,11 +141,11 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.chatListNode.peerSelected = { [weak self] peer, _, _, _ in
self?.chatListNode.clearHighlightAnimated(true)
self?.requestOpenPeer?(peer)
self?.requestOpenPeer?(peer._asPeer())
}
self.chatListNode.disabledPeerSelected = { [weak self] peer in
self?.requestOpenDisabledPeer?(peer)
self?.requestOpenDisabledPeer?(peer._asPeer())
}
self.chatListNode.contentOffsetChanged = { [weak self] offset in
@ -367,7 +367,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
var selectedPeerMap: [PeerId: Peer] = [:]
strongSelf.chatListNode.updateState { state in
selectedPeerIds = Array(state.selectedPeerIds)
selectedPeerMap = state.selectedPeerMap
selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() })
return state
}
if !selectedPeerIds.isEmpty {
@ -518,66 +518,84 @@ final class PeerSelectionControllerNode: ASDisplayNode {
}
if self.chatListNode.supernode != nil {
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, updatedPresentationData: self.updatedPresentationData, filter: self.filter, groupId: .root, displaySearchFilters: false, openPeer: { [weak self] peer, chatPeer, _ in
guard let strongSelf = self else {
return
}
var updated = false
var count = 0
strongSelf.chatListNode.updateState { state in
if state.editing {
updated = true
var state = state
var foundPeers = state.foundPeers
var selectedPeerMap = state.selectedPeerMap
selectedPeerMap[peer.id] = peer
if peer is TelegramSecretChat, let chatPeer = chatPeer {
selectedPeerMap[chatPeer.id] = chatPeer
self.searchDisplayController = SearchDisplayController(
presentationData: self.presentationData,
contentNode: ChatListSearchContainerNode(
context: self.context,
updatedPresentationData: self.updatedPresentationData,
filter: self.filter,
groupId: EngineChatList.Group(.root),
displaySearchFilters: false,
openPeer: { [weak self] peer, chatPeer, _ in
guard let strongSelf = self else {
return
}
var exists = false
for foundPeer in foundPeers {
if peer.id == foundPeer.0.id {
exists = true
break
var updated = false
var count = 0
strongSelf.chatListNode.updateState { state in
if state.editing {
updated = true
var state = state
var foundPeers = state.foundPeers
var selectedPeerMap = state.selectedPeerMap
selectedPeerMap[peer.id] = peer
if case .secretChat = peer, let chatPeer = chatPeer {
selectedPeerMap[chatPeer.id] = chatPeer
}
var exists = false
for foundPeer in foundPeers {
if peer.id == foundPeer.0.id {
exists = true
break
}
}
if !exists {
foundPeers.insert((peer, chatPeer), at: 0)
}
if state.selectedPeerIds.contains(peer.id) {
state.selectedPeerIds.remove(peer.id)
} else {
state.selectedPeerIds.insert(peer.id)
}
state.foundPeers = foundPeers
state.selectedPeerMap = selectedPeerMap
count = state.selectedPeerIds.count
return state
} else {
return state
}
}
if !exists {
foundPeers.insert((peer, chatPeer), at: 0)
if updated {
strongSelf.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true)
strongSelf.requestDeactivateSearch?()
} else if let requestOpenPeerFromSearch = strongSelf.requestOpenPeerFromSearch {
requestOpenPeerFromSearch(peer._asPeer())
}
if state.selectedPeerIds.contains(peer.id) {
state.selectedPeerIds.remove(peer.id)
} else {
state.selectedPeerIds.insert(peer.id)
},
openDisabledPeer: { [weak self] peer in
self?.requestOpenDisabledPeer?(peer._asPeer())
},
openRecentPeerOptions: { _ in
},
openMessage: { [weak self] peer, messageId, _ in
if let requestOpenMessageFromSearch = self?.requestOpenMessageFromSearch {
requestOpenMessageFromSearch(peer._asPeer(), messageId)
}
state.foundPeers = foundPeers
state.selectedPeerMap = selectedPeerMap
count = state.selectedPeerIds.count
return state
} else {
return state
},
addContact: nil,
peerContextAction: nil,
present: { [weak self] c, a in
self?.present(c, a)
},
presentInGlobalOverlay: { _, _ in
},
navigationController: nil
), cancel: { [weak self] in
if let requestDeactivateSearch = self?.requestDeactivateSearch {
requestDeactivateSearch()
}
}
if updated {
strongSelf.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true)
strongSelf.requestDeactivateSearch?()
} else if let requestOpenPeerFromSearch = strongSelf.requestOpenPeerFromSearch {
requestOpenPeerFromSearch(peer)
}
}, openDisabledPeer: { [weak self] peer in
self?.requestOpenDisabledPeer?(peer)
}, openRecentPeerOptions: { _ in
}, openMessage: { [weak self] peer, messageId, _ in
if let requestOpenMessageFromSearch = self?.requestOpenMessageFromSearch {
requestOpenMessageFromSearch(peer, messageId)
}
}, addContact: nil, peerContextAction: nil, present: { [weak self] c, a in
self?.present(c, a)
}, presentInGlobalOverlay: { _, _ in
}, navigationController: nil), cancel: { [weak self] in
if let requestDeactivateSearch = self?.requestDeactivateSearch {
requestDeactivateSearch()
}
})
)
self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate)
self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in

View File

@ -1118,12 +1118,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
openExternalUrlImpl(context: context, urlContext: urlContext, url: url, forceExternal: forceExternal, presentationData: presentationData, navigationController: navigationController, dismissInput: dismissInput)
}
public func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>) -> Signal<ChatAvailableMessageActions, NoError> {
public func chatAvailableMessageActions(postbox: Postbox, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>) -> Signal<ChatAvailableMessageActions, NoError> {
return chatAvailableMessageActionsImpl(postbox: postbox, accountPeerId: accountPeerId, messageIds: messageIds)
}
public func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>, messages: [MessageId: Message] = [:], peers: [PeerId: Peer] = [:]) -> Signal<ChatAvailableMessageActions, NoError> {
return chatAvailableMessageActionsImpl(postbox: postbox, accountPeerId: accountPeerId, messageIds: messageIds, messages: messages, peers: peers)
public func chatAvailableMessageActions(postbox: Postbox, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>, messages: [EngineMessage.Id: EngineMessage] = [:], peers: [EnginePeer.Id: EnginePeer] = [:]) -> Signal<ChatAvailableMessageActions, NoError> {
return chatAvailableMessageActionsImpl(postbox: postbox, accountPeerId: accountPeerId, messageIds: messageIds, messages: messages.mapValues({ $0._asMessage() }), peers: peers.mapValues({ $0._asPeer() }))
}
public func navigateToChatController(_ params: NavigateToChatControllerParams) {

View File

@ -126,7 +126,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
let peerSignal = context.account.postbox.loadedPeerWithId(peerId)
let _ = (peerSignal
|> deliverOnMainQueue).start(next: { peer in
let searchController = HashtagSearchController(context: context, peer: peer, query: hashtag)
let searchController = HashtagSearchController(context: context, peer: EnginePeer(peer), query: hashtag)
(controller.navigationController as? NavigationController)?.pushViewController(searchController)
})
}