Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2022-11-08 15:11:46 +04:00
commit 3206796f7c
31 changed files with 396 additions and 213 deletions

View file

@ -378,6 +378,16 @@ public enum ChatControllerActivateInput {
case entityInput
}
public struct ChatNavigationStackItem: Hashable {
public var peerId: EnginePeer.Id
public var threadId: Int64?
public init(peerId: EnginePeer.Id, threadId: Int64?) {
self.peerId = peerId
self.threadId = threadId
}
}
public final class NavigateToChatControllerParams {
public enum Location {
case peer(EnginePeer)
@ -434,12 +444,12 @@ public final class NavigateToChatControllerParams {
public let options: NavigationAnimationOptions
public let parentGroupId: PeerGroupId?
public let chatListFilter: Int32?
public let chatNavigationStack: [PeerId]
public let chatNavigationStack: [ChatNavigationStackItem]
public let changeColors: Bool
public let setupController: (ChatController) -> Void
public let completion: (ChatController) -> Void
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [PeerId] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, completion: @escaping (ChatController) -> Void = { _ in }) {
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, completion: @escaping (ChatController) -> Void = { _ in }) {
self.navigationController = navigationController
self.chatController = chatController
self.chatLocationContextHolder = chatLocationContextHolder

View file

@ -580,6 +580,14 @@ public enum ChatPresentationInputQueryResult: Equatable {
public let ChatControllerCount = Atomic<Int32>(value: 0)
public final class PeerInfoNavigationSourceTag {
public let peerId: EnginePeer.Id
public init(peerId: EnginePeer.Id) {
self.peerId = peerId
}
}
public protocol PeerInfoScreen: ViewController {
}

View file

@ -86,7 +86,18 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?,
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._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, clipStyle: round ? .round : .none, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) {
let clipStyle: AvatarNodeClipStyle
if round {
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
clipStyle = .roundedRect
} else {
clipStyle = .round
}
} else {
clipStyle = .none
}
if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, clipStyle: clipStyle, 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

View file

@ -419,6 +419,17 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, isViewingAsTopics: true, sourceView: sourceNode.view, gesture: gesture)
}
self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside)
/*self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
self.navigationBar?.allowsCustomTransition = { [weak self] in
guard let strongSelf = self else {
return false
}
if strongSelf.navigationBar?.userInfo == nil {
return false
}
return true
}*/
}
switch self.location {

View file

@ -1273,7 +1273,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
#if DEBUG
//debugSaveState(basePath: basePath + "/db", name: "previous1")
debugRestoreState(basePath: basePath + "/db", name: "previous1")
//debugRestoreState(basePath: basePath + "/db", name: "previous1")
#endif
let startTime = CFAbsoluteTimeGetCurrent()

View file

@ -1029,7 +1029,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[649453030] = { return Api.messages.MessageEditData.parse_messageEditData($0) }
dict[834488621] = { return Api.messages.MessageReactionsList.parse_messageReactionsList($0) }
dict[-1228606141] = { return Api.messages.MessageViews.parse_messageViews($0) }
dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) }
dict[-948520370] = { return Api.messages.Messages.parse_channelMessages($0) }
dict[-1938715001] = { return Api.messages.Messages.parse_messages($0) }
dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) }
dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) }

View file

@ -1374,16 +1374,16 @@ public extension Api.messages {
}
public extension Api.messages {
enum Messages: TypeConstructorDescription {
case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
case channelMessages(flags: Int32, pts: Int32, count: Int32, offsetIdOffset: Int32?, messages: [Api.Message], topics: [Api.ForumTopic], chats: [Api.Chat], users: [Api.User])
case messages(messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
case messagesNotModified(count: Int32)
case messagesSlice(flags: Int32, count: Int32, nextRate: Int32?, offsetIdOffset: Int32?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let chats, let users):
case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users):
if boxed {
buffer.appendInt32(1682413576)
buffer.appendInt32(-948520370)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(pts, buffer: buffer, boxed: false)
@ -1395,6 +1395,11 @@ public extension Api.messages {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(topics.count))
for item in topics {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
@ -1460,8 +1465,8 @@ public extension Api.messages {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let chats, let users):
return ("channelMessages", [("flags", String(describing: flags)), ("pts", String(describing: pts)), ("count", String(describing: count)), ("offsetIdOffset", String(describing: offsetIdOffset)), ("messages", String(describing: messages)), ("chats", String(describing: chats)), ("users", String(describing: users))])
case .channelMessages(let flags, let pts, let count, let offsetIdOffset, let messages, let topics, let chats, let users):
return ("channelMessages", [("flags", String(describing: flags)), ("pts", String(describing: pts)), ("count", String(describing: count)), ("offsetIdOffset", String(describing: offsetIdOffset)), ("messages", String(describing: messages)), ("topics", String(describing: topics)), ("chats", String(describing: chats)), ("users", String(describing: users))])
case .messages(let messages, let chats, let users):
return ("messages", [("messages", String(describing: messages)), ("chats", String(describing: chats)), ("users", String(describing: users))])
case .messagesNotModified(let count):
@ -1484,13 +1489,17 @@ public extension Api.messages {
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
}
var _6: [Api.Chat]?
var _6: [Api.ForumTopic]?
if let _ = reader.readInt32() {
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self)
}
var _7: [Api.User]?
var _7: [Api.Chat]?
if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _8: [Api.User]?
if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
@ -1499,8 +1508,9 @@ public extension Api.messages {
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, chats: _6!, users: _7!)
let _c8 = _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!)
}
else {
return nil

View file

@ -485,7 +485,13 @@ enum LoadMessageHistoryThreadsError {
func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, offsetIndex: StoredPeerThreadCombinedState.Index?, limit: Int) -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> {
let signal: Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> = postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
guard let channel = transaction.getPeer(peerId) as? TelegramChannel else {
return nil
}
if !channel.flags.contains(.isForum) {
return nil
}
return apiInputChannel(channel)
}
|> castError(LoadMessageHistoryThreadsError.self)
|> mapToSignal { inputChannel -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> in

View file

@ -38,7 +38,7 @@ public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: M
apiUsers = users
case let .messagesSlice(_, _, _, _, _, _, users):
apiUsers = users
case let .channelMessages(_, _, _, _, _, _, users):
case let .channelMessages(_, _, _, _, _, _, _, users):
apiUsers = users
case .messagesNotModified:
apiUsers = []

View file

@ -14,17 +14,17 @@ public enum InternalUpdaterError {
public func requestUpdatesXml(account: Account, source: String) -> Signal<Data, InternalUpdaterError> {
return TelegramEngine(account: account).peers.resolvePeerByName(name: source)
|> castError(InternalUpdaterError.self)
|> mapToSignal { peer -> Signal<Peer?, InternalUpdaterError> in
return .single(peer?._asPeer())
|> map { peer -> Peer? in
return peer?._asPeer()
}
|> mapToSignal { peer in
|> mapToSignal { peer -> Signal<Data, InternalUpdaterError> in
if let peer = peer, let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: 0, addOffset: 0, limit: 1, maxId: Int32.max, minId: 0, hash: 0))
|> retryRequest
|> castError(InternalUpdaterError.self)
|> mapToSignal { result in
switch result {
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, _, apiChats, apiUsers):
if let apiMessage = apiMessages.first, let storeMessage = StoreMessage(apiMessage: apiMessage) {
var peers: [PeerId: Peer] = [:]
@ -67,7 +67,7 @@ public func requestUpdatesXml(account: Account, source: String) -> Signal<Data,
} else {
return .fail(.xmlLoad)
}
}
}
}
public enum AppUpdateDownloadResult {
@ -89,7 +89,7 @@ public func downloadAppUpdate(account: Account, source: String, messageId: Int32
|> castError(InternalUpdaterError.self)
|> mapToSignal { messages in
switch messages {
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, _, apiChats, apiUsers):
var peers: [PeerId: Peer] = [:]
for chat in apiChats {

View file

@ -848,7 +848,12 @@ func multipartFetch(postbox: Postbox, network: Network, mediaReferenceRevalidati
} else {
return .revalidate
}
case .messageAuthorAvatar:
case let .messageAuthorAvatar(message, _):
if let peer = message.author {
if let location = resource.apiInputLocation(peerReference: peer) {
return .location(location)
}
}
return .revalidate
default:
return .none

View file

@ -2086,7 +2086,8 @@ private func resolveAssociatedMessages(postbox: Postbox, network: Network, state
return (messages, chats, users)
case let .messagesSlice(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, _, messages, chats, users):
case let .channelMessages(_, _, _, _, messages, apiTopics, chats, users):
let _ = apiTopics
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])

View file

@ -93,8 +93,9 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal<Void
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
case .messagesNotModified:
@ -330,14 +331,14 @@ public final class AccountViewTracker {
private let externallyUpdatedPeerIdDisposable = MetaDisposable()
public let chatListPreloadItems = Promise<[ChatHistoryPreloadItem]>([])
public let chatListPreloadItems = Promise<Set<ChatHistoryPreloadItem>>([])
init(account: Account) {
self.account = account
self.historyViewStateValidationContexts = HistoryViewStateValidationContexts(queue: self.queue, postbox: account.postbox, network: account.network, accountPeerId: account.peerId)
self.chatHistoryPreloadManager = ChatHistoryPreloadManager(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, networkState: account.networkState, preloadItemsSignal: self.chatListPreloadItems.get() |> distinctUntilChanged)
self.chatHistoryPreloadManager = ChatHistoryPreloadManager(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, networkState: account.networkState, preloadItemsSignal: self.chatListPreloadItems.get() |> distinctUntilChanged |> map { Array($0) })
self.externallyUpdatedPeerIdDisposable.set((account.stateManager.externallyUpdatedPeerIds
|> deliverOn(self.queue)).start(next: { [weak self] peerIds in
@ -1070,7 +1071,7 @@ public final class AccountViewTracker {
return (messages, chats, users)
case let .messagesSlice(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, _, messages, chats, users):
case let .channelMessages(_, _, _, _, messages, _, chats, users):
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])
@ -1712,7 +1713,9 @@ public final class AccountViewTracker {
|> mapToSignal { threadInfo -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in
if let threadInfo = threadInfo {
let anchor: HistoryViewInputAnchor
if threadInfo.incomingUnreadCount > 0 && tagMask == nil {
if threadInfo.maxIncomingReadId <= 1 {
anchor = .message(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: 1))
} else if threadInfo.incomingUnreadCount > 0 && tagMask == nil {
let customUnreadMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: threadInfo.maxIncomingReadId)
anchor = .message(customUnreadMessageId)
} else {

View file

@ -247,12 +247,19 @@ private final class AdditionalPreloadPeerIdsContext {
}
}
public struct ChatHistoryPreloadItem : Equatable {
public struct ChatHistoryPreloadItem : Equatable, Hashable {
public let index: ChatListIndex
public let threadId: Int64?
public let isMuted: Bool
public let hasUnread: Bool
public func hash(into hasher: inout Hasher) {
hasher.combine(index.hashValue)
if let threadId = threadId {
hasher.combine(threadId)
}
}
public init(index: ChatListIndex, threadId: Int64?, isMuted: Bool, hasUnread: Bool) {
self.index = index
self.threadId = threadId

View file

@ -522,8 +522,9 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, pts, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
channelPts = pts
@ -540,7 +541,8 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac
}
return validateBatch(postbox: postbox, network: network, transaction: transaction, accountPeerId: accountPeerId, tag: tag, historyState: historyState, signal: signal, previous: previous, messageNamespace: Namespaces.Message.Cloud)
} |> switchToLatest
}
|> switchToLatest
}
private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadMessageId: Int32, tag: MessageTags?, messageIds: [MessageId]) -> Signal<Void, NoError> {
@ -589,8 +591,9 @@ private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, pts, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
channelPts = pts
@ -630,8 +633,9 @@ private func validateScheduledMessagesBatch(postbox: Postbox, network: Network,
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
case .messagesNotModified:
@ -703,8 +707,9 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran
|> map { result -> Set<MessageId> in
let apiMessages: [Api.Message]
switch result {
case let .channelMessages(_, _, _, _, messages, _, _):
case let .channelMessages(_, _, _, _, messages, apiTopics, _, _):
apiMessages = messages
let _ = apiTopics
case let .messages(messages, _, _):
apiMessages = messages
case let .messagesSlice(_, _, _, _, messages, _, _):
@ -951,8 +956,9 @@ private func validateReplyThreadBatch(postbox: Postbox, network: Network, transa
|> map { result -> Set<MessageId> in
let apiMessages: [Api.Message]
switch result {
case let .channelMessages(_, _, _, _, messages, _, _):
case let .channelMessages(_, _, _, _, messages, apiTopics, _, _):
apiMessages = messages
let _ = apiTopics
case let .messages(messages, _, _):
apiMessages = messages
case let .messagesSlice(_, _, _, _, messages, _, _):

View file

@ -143,7 +143,8 @@ private func withResolvedAssociatedMessages<T>(postbox: Postbox, source: FetchMe
return (messages, chats, users)
case let .messagesSlice(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, _, messages, chats, users):
case let .channelMessages(_, _, _, _, messages, apiTopics, chats, users):
let _ = apiTopics
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])
@ -575,8 +576,9 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, pts, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
channelPts = pts
@ -829,8 +831,9 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
case .messagesNotModified:

View file

@ -128,7 +128,7 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox
switch result {
case let .messages(messages, _, _):
return .single(messages.compactMap({ $0.id() }))
case let .channelMessages(_, _, _, _, messages, _, _):
case let .channelMessages(_, _, _, _, messages, _, _, _):
return .single(messages.compactMap({ $0.id() }))
case .messagesNotModified:
return .single([])

View file

@ -47,7 +47,7 @@ private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId
}
let apiMessages: [Api.Message]
switch result {
case let .channelMessages(_, _, _, _, messages, _, _):
case let .channelMessages(_, _, _, _, messages, _, _, _):
apiMessages = messages
case let .messages(messages, _, _):
apiMessages = messages

View file

@ -7,16 +7,24 @@ public struct MessageReference: PostboxCoding, Hashable, Equatable {
switch content {
case .none:
return nil
case let .message(peer, _, _, _, _):
case let .message(peer, _, _, _, _, _):
return peer
}
}
public var author: PeerReference? {
switch content {
case .none:
return nil
case let .message(_, author, _, _, _, _):
return author
}
}
public var id: MessageId? {
switch content {
case .none:
return nil
case let .message(_, id, _, _, _):
case let .message(_, _, id, _, _, _):
return id
}
}
@ -25,7 +33,7 @@ public struct MessageReference: PostboxCoding, Hashable, Equatable {
switch content {
case .none:
return nil
case let .message(_, _, timestamp, _, _):
case let .message(_, _, _, timestamp, _, _):
return timestamp
}
}
@ -34,7 +42,7 @@ public struct MessageReference: PostboxCoding, Hashable, Equatable {
switch content {
case .none:
return nil
case let .message(_, _, _, incoming, _):
case let .message(_, _, _, _, incoming, _):
return incoming
}
}
@ -43,22 +51,34 @@ public struct MessageReference: PostboxCoding, Hashable, Equatable {
switch content {
case .none:
return nil
case let .message(_, _, _, _, secret):
case let .message(_, _, _, _, _, secret):
return secret
}
}
public init(_ message: Message) {
if message.id.namespace != Namespaces.Message.Local, let peer = message.peers[message.id.peerId], let inputPeer = PeerReference(peer) {
self.content = .message(peer: inputPeer, id: message.id, timestamp: message.timestamp, incoming: message.flags.contains(.Incoming), secret: message.containsSecretMedia)
let author: PeerReference?
if let peer = message.author {
author = PeerReference(peer)
} else {
author = nil
}
self.content = .message(peer: inputPeer, author: author, id: message.id, timestamp: message.timestamp, incoming: message.flags.contains(.Incoming), secret: message.containsSecretMedia)
} else {
self.content = .none
}
}
public init(peer: Peer, id: MessageId, timestamp: Int32, incoming: Bool, secret: Bool) {
public init(peer: Peer, author: Peer?, id: MessageId, timestamp: Int32, incoming: Bool, secret: Bool) {
if let inputPeer = PeerReference(peer) {
self.content = .message(peer: inputPeer, id: id, timestamp: timestamp, incoming: incoming, secret: secret)
let a: PeerReference?
if let peer = author {
a = PeerReference(peer)
} else {
a = nil
}
self.content = .message(peer: inputPeer, author: a, id: id, timestamp: timestamp, incoming: incoming, secret: secret)
} else {
self.content = .none
}
@ -75,14 +95,14 @@ public struct MessageReference: PostboxCoding, Hashable, Equatable {
public enum MessageReferenceContent: PostboxCoding, Hashable, Equatable {
case none
case message(peer: PeerReference, id: MessageId, timestamp: Int32, incoming: Bool, secret: Bool)
case message(peer: PeerReference, author: PeerReference?, id: MessageId, timestamp: Int32, incoming: Bool, secret: Bool)
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("_r", orElse: 0) {
case 0:
self = .none
case 1:
self = .message(peer: decoder.decodeObjectForKey("p", decoder: { PeerReference(decoder: $0) }) as! PeerReference, id: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("i.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt32ForKey("i.i", orElse: 0)), timestamp: 0, incoming: false, secret: false)
self = .message(peer: decoder.decodeObjectForKey("p", decoder: { PeerReference(decoder: $0) }) as! PeerReference, author: decoder.decodeObjectForKey("author") as? PeerReference, id: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("i.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt32ForKey("i.i", orElse: 0)), timestamp: 0, incoming: false, secret: false)
default:
assertionFailure()
self = .none
@ -93,9 +113,14 @@ public enum MessageReferenceContent: PostboxCoding, Hashable, Equatable {
switch self {
case .none:
encoder.encodeInt32(0, forKey: "_r")
case let .message(peer, id, _, _, _):
case let .message(peer, author, id, _, _, _):
encoder.encodeInt32(1, forKey: "_r")
encoder.encodeObject(peer, forKey: "p")
if let author = author {
encoder.encodeObject(author, forKey: "author")
} else {
encoder.encodeNil(forKey: "author")
}
encoder.encodeInt64(id.peerId.toInt64(), forKey: "i.p")
encoder.encodeInt32(id.namespace, forKey: "i.n")
encoder.encodeInt32(id.id, forKey: "i.i")

View file

@ -905,11 +905,25 @@ public extension TelegramEngine.EngineData.Item {
}
}
public struct ThreadData: TelegramEngineDataItem, PostboxViewDataItem {
public struct ThreadData: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public struct Key: Hashable {
public var id: EnginePeer.Id
public var threadId: Int64
public init(id: EnginePeer.Id, threadId: Int64) {
self.id = id
self.threadId = threadId
}
}
public typealias Result = MessageHistoryThreadData?
fileprivate var id: EnginePeer.Id
fileprivate var threadId: Int64
public var mapKey: Key {
return Key(id: self.id, threadId: self.threadId)
}
public init(id: EnginePeer.Id, threadId: Int64) {
self.id = id

View file

@ -66,7 +66,8 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po
return (messages, chats, users)
case let .messagesSlice(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, _, messages, chats, users):
case let .channelMessages(_, _, _, _, messages, apiTopics, chats, users):
let _ = apiTopics
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])

View file

@ -74,8 +74,9 @@ private func mergedState(transaction: Transaction, state: SearchMessagesPeerStat
let totalCount: Int32
let nextRate: Int32?
switch result {
case let .channelMessages(_, _, count, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, count, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
totalCount = count
@ -478,8 +479,9 @@ func _internal_downloadMessage(postbox: Postbox, network: Network, messageId: Me
let chats: [Api.Chat]
let users: [Api.User]
switch result {
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
case let .messages(apiMessages, apiChats, apiUsers):
@ -531,7 +533,7 @@ func _internal_downloadMessage(postbox: Postbox, network: Network, messageId: Me
}
func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource, message: MessageReference) -> Signal<Message?, NoError> {
guard case let .message(peer, id, _, _, _) = message.content else {
guard case let .message(peer, _, id, _, _, _) = message.content else {
return .single(nil)
}
let signal: Signal<Api.messages.Messages, MTRpcError>
@ -562,8 +564,9 @@ func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource,
let chats: [Api.Chat]
let users: [Api.User]
switch result {
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, apiTopics, apiChats, apiUsers):
messages = apiMessages
let _ = apiTopics
chats = apiChats
users = apiUsers
case let .messages(apiMessages, apiChats, apiUsers):
@ -638,7 +641,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre
switch result {
case let .messages(apiMessages, _, _):
messages = apiMessages
case let .channelMessages(_, _, _, _, apiMessages, _, _):
case let .channelMessages(_, _, _, _, apiMessages, _, _, _):
messages = apiMessages
case let .messagesSlice(_, _, _, _, apiMessages, _, _):
messages = apiMessages
@ -668,7 +671,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre
switch result {
case let .messages(apiMessages, _, _):
messages = apiMessages
case let .channelMessages(_, _, _, _, apiMessages, _, _):
case let .channelMessages(_, _, _, _, apiMessages, _, _, _):
messages = apiMessages
case let .messagesSlice(_, _, _, _, apiMessages, _, _):
messages = apiMessages
@ -692,7 +695,7 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre
switch result {
case let .messages(apiMessages, _, _):
messages = apiMessages
case let .channelMessages(_, _, _, _, apiMessages, _, _):
case let .channelMessages(_, _, _, _, apiMessages, _, _, _):
messages = apiMessages
case let .messagesSlice(_, _, _, _, apiMessages, _, _):
messages = apiMessages

View file

@ -712,7 +712,7 @@ public final class SparseMessageList {
}
}
public final class SparseMessageScrollingContext {
/*public final class SparseMessageScrollingContext {
public struct State: Equatable {
public var totalCount: Int
public var minTimestamp: Int32
@ -762,7 +762,7 @@ public final class SparseMessageScrollingContext {
case let .messagesSlice(_, count, _, _, apiMessages, _, _):
messages = apiMessages
totalCount = Int(count)
case let .channelMessages(_, _, count, _, apiMessages, _, _):
case let .channelMessages(_, _, count, _, apiMessages, _, _, _):
messages = apiMessages
totalCount = Int(count)
case .messagesNotModified:
@ -815,7 +815,7 @@ public final class SparseMessageScrollingContext {
return Impl(queue: queue, account: account, peerId: peerId)
})
}
}
}*/
public final class SparseMessageCalendar {
private final class Impl {

View file

@ -293,9 +293,9 @@ public extension TelegramEngine {
return SparseMessageCalendar(account: self.account, peerId: peerId, threadId: threadId, messageTag: tag)
}
public func sparseMessageScrollingContext(peerId: EnginePeer.Id) -> SparseMessageScrollingContext {
/*public func sparseMessageScrollingContext(peerId: EnginePeer.Id) -> SparseMessageScrollingContext {
return SparseMessageScrollingContext(account: self.account, peerId: peerId)
}
}*/
public func refreshMessageTagStats(peerId: EnginePeer.Id, threadId: Int64?, tags: [EngineMessage.Tags]) -> Signal<Never, NoError> {
let account = self.account
@ -325,7 +325,7 @@ public extension TelegramEngine {
switch result {
case let .messagesSlice(_, count, _, _, messages, _, _):
return (count, messages.first?.id(namespace: Namespaces.Message.Cloud)?.id)
case let .channelMessages(_, _, count, _, messages, _, _):
case let .channelMessages(_, _, count, _, messages, _, _, _):
return (count, messages.first?.id(namespace: Namespaces.Message.Cloud)?.id)
case let .messages(messages, _, _):
return (Int32(messages.count), messages.first?.id(namespace: Namespaces.Message.Cloud)?.id)

View file

@ -79,7 +79,7 @@ func _internal_requestPeerPhotos(postbox: Postbox, network: Network, peerId: Pee
let chats: [Api.Chat]
let users: [Api.User]
switch result {
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
case let .channelMessages(_, _, _, _, apiMessages, _, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers

View file

@ -219,7 +219,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
public weak var parentController: ViewController?
private let currentChatListFilter: Int32?
private let chatNavigationStack: [PeerId]
private let chatNavigationStack: [ChatNavigationStackItem]
public var peekActions: ChatControllerPeekActions = .standard
private var didSetup3dTouch: Bool = false
@ -494,8 +494,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
override public var customNavigationData: CustomViewControllerNavigationData? {
get {
if case let .peer(peerId) = self.chatLocation {
return ChatControllerNavigationData(peerId: peerId)
if let peerId = self.chatLocation.peerId {
return ChatControllerNavigationData(peerId: peerId, threadId: self.chatLocation.threadId)
} else {
return nil
}
@ -534,7 +534,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return title
}
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [PeerId] = []) {
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = []) {
let _ = ChatControllerCount.modify { value in
return value + 1
}
@ -4078,19 +4078,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.controllerInteraction = controllerInteraction
if case let .peer(peerId) = chatLocation, peerId != context.account.peerId {
switch subject {
case .pinnedMessages, .scheduledMessages, .forwardedMessages:
break
default:
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
if chatLocation.threadId == nil {
if let peerId = chatLocation.peerId, peerId != context.account.peerId {
switch subject {
case .pinnedMessages, .scheduledMessages, .forwardedMessages:
break
default:
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
}
}
}
self.navigationBar?.allowsCustomTransition = { [weak self] in
guard let strongSelf = self else {
return false
self.navigationBar?.allowsCustomTransition = { [weak self] in
guard let strongSelf = self else {
return false
}
if strongSelf.navigationBar?.userInfo == nil {
return false
}
if strongSelf.navigationBar?.contentNode != nil {
return false
}
return true
}
return strongSelf.navigationItem.rightBarButtonItem === strongSelf.chatInfoNavigationButton?.buttonItem
}
self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer)
@ -9869,9 +9877,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
var updatedChatNavigationStack = strongSelf.chatNavigationStack
updatedChatNavigationStack.removeAll(where: { $0 == peer.id})
if case let .peer(peerId) = strongSelf.chatLocation {
updatedChatNavigationStack.insert(peerId, at: 0)
updatedChatNavigationStack.removeAll(where: { $0 == ChatNavigationStackItem(peerId: peer.id, threadId: nil) })
if let peerId = strongSelf.chatLocation.peerId {
updatedChatNavigationStack.insert(ChatNavigationStackItem(peerId: peerId, threadId: strongSelf.chatLocation.threadId), at: 0)
}
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), animated: false, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in
@ -9935,73 +9943,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
var chatNavigationStack: [PeerId] = self.chatNavigationStack
if case let .peer(peerId) = self.chatLocation, let summary = self.customNavigationDataSummary as? ChatControllerNavigationDataSummary {
chatNavigationStack.removeAll()
chatNavigationStack = summary.peerIds.filter({ $0 != peerId })
var chatNavigationStack: [ChatNavigationStackItem] = self.chatNavigationStack
if let peerId = self.chatLocation.peerId {
if let summary = self.customNavigationDataSummary as? ChatControllerNavigationDataSummary {
chatNavigationStack.removeAll()
chatNavigationStack = summary.peerNavigationItems.filter({ $0 != ChatNavigationStackItem(peerId: peerId, threadId: self.chatLocation.threadId) })
}
if let _ = self.chatLocation.threadId {
if !chatNavigationStack.contains(ChatNavigationStackItem(peerId: peerId, threadId: nil)) {
chatNavigationStack.append(ChatNavigationStackItem(peerId: peerId, threadId: nil))
}
}
}
if !chatNavigationStack.isEmpty {
self.chatDisplayNode.navigationBar?.backButtonNode.isGestureEnabled = true
self.chatDisplayNode.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
guard let strongSelf = self, let backButtonNode = strongSelf.chatDisplayNode.navigationBar?.backButtonNode, let navigationController = strongSelf.effectiveNavigationController else {
gesture.cancel()
return
}
let _ = (strongSelf.context.engine.data.get(EngineDataList(
chatNavigationStack.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
))
|> deliverOnMainQueue).start(next: { peerList in
guard let strongSelf = self, let backButtonNode = strongSelf.chatDisplayNode.navigationBar?.backButtonNode else {
return
}
let peers = peerList.compactMap { $0 }
let avatarSize = CGSize(width: 28.0, height: 28.0)
var items: [ContextMenuItem] = []
for peer in peers {
let title: String
let iconSource: ContextMenuActionItemIconSource?
if peer.id == strongSelf.context.account.peerId {
title = strongSelf.presentationData.strings.DialogList_SavedMessages
iconSource = nil
} else {
title = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
iconSource = ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer, size: avatarSize))
}
let isSavedMessages = peer.id == strongSelf.context.account.peerId
items.append(.action(ContextMenuActionItem(text: title, icon: { _ in
if isSavedMessages {
return generateAvatarImage(size: avatarSize, icon: savedMessagesIcon, iconScale: 0.5, color: .blue)
}
return nil
}, iconSource: iconSource, action: { _, f in
f(.default)
guard let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController else {
return
}
let nextFolderId: Int32? = strongSelf.currentChatListFilter
var updatedChatNavigationStack = strongSelf.chatNavigationStack
if let index = updatedChatNavigationStack.firstIndex(of: peer.id) {
updatedChatNavigationStack.removeSubrange(0 ..< (index + 1))
}
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), useBackAnimation: true, animated: true, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in
let _ = nextController
}))
})))
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: backButtonNode.view, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
})
let nextFolderId: Int32? = strongSelf.currentChatListFilter
PeerInfoScreenImpl.displayChatNavigationMenu(
context: strongSelf.context,
chatNavigationStack: chatNavigationStack,
nextFolderId: nextFolderId,
parentController: strongSelf,
backButtonView: backButtonNode.view,
navigationController: navigationController,
gesture: gesture
)
}
}
}
@ -10454,6 +10425,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState)
}
interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage)
if case .peer = self.chatLocation, let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) {
interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState())
}
let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { _ in
return interfaceState
}).start()
@ -17632,33 +17606,35 @@ func peerMessageSelectedReactions(context: AccountContext, message: Message) ->
final class ChatControllerNavigationData: CustomViewControllerNavigationData {
let peerId: PeerId
let threadId: Int64?
init(peerId: PeerId) {
init(peerId: PeerId, threadId: Int64?) {
self.peerId = peerId
self.threadId = threadId
}
func combine(summary: CustomViewControllerNavigationDataSummary?) -> CustomViewControllerNavigationDataSummary? {
if let summary = summary as? ChatControllerNavigationDataSummary {
return summary.adding(peerId: self.peerId)
return summary.adding(peerNavigationItem: ChatNavigationStackItem(peerId: self.peerId, threadId: threadId))
} else {
return ChatControllerNavigationDataSummary(peerIds: [self.peerId])
return ChatControllerNavigationDataSummary(peerNavigationItems: [ChatNavigationStackItem(peerId: self.peerId, threadId: threadId)])
}
}
}
final class ChatControllerNavigationDataSummary: CustomViewControllerNavigationDataSummary {
let peerIds: [PeerId]
let peerNavigationItems: [ChatNavigationStackItem]
init(peerIds: [PeerId]) {
self.peerIds = peerIds
init(peerNavigationItems: [ChatNavigationStackItem]) {
self.peerNavigationItems = peerNavigationItems
}
func adding(peerId: PeerId) -> ChatControllerNavigationDataSummary {
var peerIds = self.peerIds
if let index = peerIds.firstIndex(of: peerId) {
peerIds.removeSubrange(0 ... index)
func adding(peerNavigationItem: ChatNavigationStackItem) -> ChatControllerNavigationDataSummary {
var peerNavigationItems = self.peerNavigationItems
if let index = peerNavigationItems.firstIndex(of: peerNavigationItem) {
peerNavigationItems.removeSubrange(0 ... index)
}
peerIds.insert(peerId, at: 0)
return ChatControllerNavigationDataSummary(peerIds: peerIds)
peerNavigationItems.insert(peerNavigationItem, at: 0)
return ChatControllerNavigationDataSummary(peerNavigationItems: peerNavigationItems)
}
}

View file

@ -110,6 +110,12 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess
canScrollToRead = false
}
if case let .replyThread(message) = chatLocation, message.isForumPost {
if case let .message(index) = view.anchorIndex {
scrollPosition = .index(index: .message(index), position: .top(0.0), directionHint: .Up, animated: false, highlight: false, displayLink: false)
}
}
if let maxReadIndex = view.maxReadIndex, tagMask == nil, canScrollToRead {
let aroundIndex = maxReadIndex
scrollPosition = .unread(index: maxReadIndex)

View file

@ -2536,7 +2536,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var transitionSourceHeight: CGFloat = 0.0
var transitionFraction: CGFloat = 0.0
var transitionSourceAvatarFrame = CGRect()
var transitionSourceAvatarFrame: CGRect?
var transitionSourceTitleFrame = CGRect()
var transitionSourceSubtitleFrame = CGRect()
@ -2544,10 +2544,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let headerBackgroundColor: UIColor = presentationData.theme.list.blocksBackgroundColor
var effectiveSeparatorAlpha: CGFloat
if let navigationTransition = self.navigationTransition, let sourceAvatarNode = (navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode)?.avatarNode {
if let navigationTransition = self.navigationTransition {
transitionSourceHeight = navigationTransition.sourceNavigationBar.backgroundNode.bounds.height
transitionFraction = navigationTransition.fraction
transitionSourceAvatarFrame = sourceAvatarNode.view.convert(sourceAvatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view)
if let sourceAvatarNode = (navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode)?.avatarNode {
transitionSourceAvatarFrame = sourceAvatarNode.view.convert(sourceAvatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view)
}
transitionSourceTitleFrame = navigationTransition.sourceTitleFrame
transitionSourceSubtitleFrame = navigationTransition.sourceSubtitleFrame
@ -2761,7 +2764,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.usernameNode.accessibilityLabel = usernameString.string
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize))
let avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY)
let avatarCenter: CGPoint
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY)
} else {
avatarCenter = avatarFrame.center
}
let titleSize = titleNodeLayout[TitleNodeStateRegular]!.size
let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size
@ -2893,7 +2901,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let avatarScale: CGFloat
let avatarOffset: CGFloat
if self.navigationTransition != nil {
avatarScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * transitionSourceAvatarFrame.width) / avatarFrame.width
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
avatarScale = ((1.0 - transitionFraction) * avatarFrame.width + transitionFraction * transitionSourceAvatarFrame.width) / avatarFrame.width
} else {
avatarScale = 1.0
}
avatarOffset = 0.0
} else {
avatarScale = 1.0 * (1.0 - titleCollapseFraction) + avatarMinScale * titleCollapseFraction
@ -2909,7 +2921,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if self.isAvatarExpanded {
self.avatarListNode.listContainerNode.isHidden = false
if !transitionSourceAvatarFrame.width.isZero {
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: transitionFraction * transitionSourceAvatarFrame.width / 2.0)
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: transitionFraction * transitionSourceAvatarFrame.width / 2.0)
} else {
@ -2947,7 +2959,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if self.isAvatarExpanded {
let expandedAvatarCenter = CGPoint(x: expandedAvatarListSize.width / 2.0, y: expandedAvatarListSize.height / 2.0 - contentOffset / 2.0)
apparentAvatarFrame = CGRect(origin: CGPoint(x: expandedAvatarCenter.x * (1.0 - transitionFraction) + transitionFraction * avatarCenter.x, y: expandedAvatarCenter.y * (1.0 - transitionFraction) + transitionFraction * avatarCenter.y), size: CGSize())
if !transitionSourceAvatarFrame.width.isZero {
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
let expandedFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedAvatarListSize)
controlsClippingFrame = CGRect(origin: CGPoint(x: transitionFraction * transitionSourceAvatarFrame.minX + (1.0 - transitionFraction) * expandedFrame.minX, y: transitionFraction * transitionSourceAvatarFrame.minY + (1.0 - transitionFraction) * expandedFrame.minY), size: CGSize(width: transitionFraction * transitionSourceAvatarFrame.width + (1.0 - transitionFraction) * expandedFrame.width, height: transitionFraction * transitionSourceAvatarFrame.height + (1.0 - transitionFraction) * expandedFrame.height))
} else {
@ -2963,7 +2975,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let avatarListContainerFrame: CGRect
let avatarListContainerScale: CGFloat
if self.isAvatarExpanded {
if !transitionSourceAvatarFrame.width.isZero {
if let transitionSourceAvatarFrame = transitionSourceAvatarFrame {
let neutralAvatarListContainerSize = expandedAvatarListSize
let avatarListContainerSize = CGSize(width: neutralAvatarListContainerSize.width * (1.0 - transitionFraction) + transitionSourceAvatarFrame.width * transitionFraction, height: neutralAvatarListContainerSize.height * (1.0 - transitionFraction) + transitionSourceAvatarFrame.height * transitionFraction)
avatarListContainerFrame = CGRect(origin: CGPoint(x: -avatarListContainerSize.width / 2.0, y: -avatarListContainerSize.height / 2.0), size: avatarListContainerSize)

View file

@ -8548,7 +8548,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
override public var customNavigationData: CustomViewControllerNavigationData? {
get {
if !self.isSettings {
return ChatControllerNavigationData(peerId: self.peerId)
return ChatControllerNavigationData(peerId: self.peerId, threadId: self.chatLocation.threadId)
} else {
return nil
}
@ -8760,7 +8760,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
}
if case .peer = self.chatLocation {
if self.chatLocation.peerId != nil {
self.navigationBar?.makeCustomTransitionNode = { [weak self] other, isInteractive in
guard let strongSelf = self else {
return nil
@ -8777,9 +8777,6 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
if isInteractive && strongSelf.controllerNode.headerNode.isAvatarExpanded {
return nil
}
if other.contentNode != nil {
return nil
}
if let allowsCustomTransition = other.allowsCustomTransition, !allowsCustomTransition() {
return nil
}
@ -8924,50 +8921,122 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
}
static func displayChatNavigationMenu(context: AccountContext, chatNavigationStack: [ChatNavigationStackItem], nextFolderId: Int32?, parentController: ViewController, backButtonView: UIView, navigationController: NavigationController, gesture: ContextGesture) {
let peerMap = EngineDataMap(
Set(chatNavigationStack.map(\.peerId)).map(TelegramEngine.EngineData.Item.Peer.Peer.init)
)
let threadDataMap = EngineDataMap(
Set(chatNavigationStack.filter { $0.threadId != nil }).map { TelegramEngine.EngineData.Item.Peer.ThreadData(id: $0.peerId, threadId: $0.threadId!) }
)
let _ = (context.engine.data.get(
peerMap,
threadDataMap
)
|> deliverOnMainQueue).start(next: { [weak parentController, weak backButtonView, weak navigationController] peerMap, threadDataMap in
guard let parentController, let backButtonView else {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let avatarSize = CGSize(width: 28.0, height: 28.0)
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChatList_Tabs_AllChats, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.default)
navigationController?.popToRoot(animated: true)
})))
for item in chatNavigationStack {
guard let maybeItemPeer = peerMap[item.peerId], let itemPeer = maybeItemPeer else {
continue
}
let title: String
let iconSource: ContextMenuActionItemIconSource?
if let threadId = item.threadId {
guard let maybeThreadData = threadDataMap[TelegramEngine.EngineData.Item.Peer.ThreadData.Key(id: item.peerId, threadId: threadId)], let threadData = maybeThreadData else {
continue
}
title = threadData.info.title
iconSource = nil
} else {
if itemPeer.id == context.account.peerId {
title = presentationData.strings.DialogList_SavedMessages
iconSource = nil
} else {
title = itemPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
iconSource = ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: context.account, peer: itemPeer, size: avatarSize))
}
}
let isSavedMessages = itemPeer.id == context.account.peerId
items.append(.action(ContextMenuActionItem(text: title, icon: { _ in
if isSavedMessages {
return generateAvatarImage(size: avatarSize, icon: savedMessagesIcon, iconScale: 0.5, color: .blue)
}
return nil
}, iconSource: iconSource, action: { _, f in
f(.default)
guard let navigationController = navigationController else {
return
}
var updatedChatNavigationStack = chatNavigationStack
if let index = updatedChatNavigationStack.firstIndex(of: item) {
updatedChatNavigationStack.removeSubrange(0 ..< (index + 1))
}
let navigateChatLocation: NavigateToChatControllerParams.Location
if let threadId = item.threadId {
navigateChatLocation = .replyThread(ChatReplyThreadMessage(
messageId: MessageId(peerId: item.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
))
} else {
navigateChatLocation = .peer(itemPeer)
}
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: navigateChatLocation, useBackAnimation: true, animated: true, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { _ in
}))
})))
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
parentController.presentInGlobalOverlay(contextController)
})
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
var chatNavigationStack: [PeerId] = []
var chatNavigationStack: [ChatNavigationStackItem] = []
if !self.isSettings, let summary = self.customNavigationDataSummary as? ChatControllerNavigationDataSummary {
chatNavigationStack.removeAll()
chatNavigationStack = summary.peerIds.filter({ $0 != peerId })
chatNavigationStack = summary.peerNavigationItems.filter({ $0 != ChatNavigationStackItem(peerId: self.peerId, threadId: self.chatLocation.threadId) })
}
if !chatNavigationStack.isEmpty {
self.navigationBar?.backButtonNode.isGestureEnabled = true
self.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {
guard let strongSelf = self, let backButtonNode = strongSelf.navigationBar?.backButtonNode, let navigationController = strongSelf.navigationController as? NavigationController else {
gesture.cancel()
return
}
let _ = (strongSelf.context.engine.data.get(EngineDataList(
chatNavigationStack.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
))
|> deliverOnMainQueue).start(next: { peerList in
guard let strongSelf = self, let backButtonNode = strongSelf.navigationBar?.backButtonNode else {
return
}
let peers = peerList.compactMap { $0 }
let avatarSize = CGSize(width: 28.0, height: 28.0)
var items: [ContextMenuItem] = []
for peer in peers {
items.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), icon: { _ in return nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: peer, size: avatarSize)), action: { _, f in
f(.default)
guard let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController else {
return
}
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), animated: true, completion: { _ in
}))
})))
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: backButtonNode.view, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
})
PeerInfoScreenImpl.displayChatNavigationMenu(
context: strongSelf.context,
chatNavigationStack: chatNavigationStack,
nextFolderId: nil,
parentController: strongSelf,
backButtonView: backButtonNode.view,
navigationController: navigationController,
gesture: gesture
)
}
}
}
@ -9105,14 +9174,6 @@ private func getUserPeer(engine: TelegramEngine, peerId: EnginePeer.Id) -> Signa
}
}
final class PeerInfoNavigationSourceTag {
let peerId: PeerId
init(peerId: PeerId) {
self.peerId = peerId
}
}
private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavigationTransitionNode {
private let screenNode: PeerInfoScreenNode
private let presentationData: PresentationData
@ -9238,7 +9299,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
transition.updateAlpha(node: currentBackButton, alpha: (1.0 - fraction))
}
if let previousTitleView = bottomNavigationBar.titleView as? ChatTitleView, let _ = (bottomNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode)?.avatarNode, let (previousTitleContainerNode, previousTitleNode) = self.previousTitleNode, let (previousStatusContainerNode, previousStatusNode) = self.previousStatusNode {
if let previousTitleView = bottomNavigationBar.titleView as? ChatTitleView, let (previousTitleContainerNode, previousTitleNode) = self.previousTitleNode, let (previousStatusContainerNode, previousStatusNode) = self.previousStatusNode {
let previousTitleFrame = previousTitleView.titleContainerView.convert(previousTitleView.titleContainerView.bounds, to: bottomNavigationBar.view)
let previousStatusFrame = previousTitleView.activityNode.view.convert(previousTitleView.activityNode.bounds, to: bottomNavigationBar.view)

View file

@ -576,10 +576,12 @@ public final class OngoingGroupCallContext {
self.audioSessionActiveDisposable.set((audioSessionActive
|> deliverOn(queue)).start(next: { [weak self] isActive in
guard let self else {
guard let `self` = self else {
return
}
#if os(iOS)
self.context.setManualAudioSessionIsActive(isActive)
#endif
}))
}

View file

@ -579,7 +579,9 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
}
func nativeSetIsAudioSessionActive(isActive: Bool) {
#if os(iOS)
self.setManualAudioSessionIsActive(isActive)
#endif
}
}
@ -1482,7 +1484,7 @@ private final class CallSignalingConnectionManager {
let connection = CallSignalingConnectionImpl(queue: queue, host: host, port: port, peerTag: self.peerTag, dataReceived: { data in
dataReceived(data)
}, isClosed: { [weak self] in
guard let self else {
guard let `self` = self else {
return
}
self.handleConnectionFailed(id: id)