Commends update

This commit is contained in:
Ali 2020-09-22 21:15:41 +04:00
parent 68bd4529bb
commit adbf2534b0
36 changed files with 370 additions and 132 deletions

View File

@ -38,6 +38,7 @@ public func openUserGeneratedUrl(context: AccountContext, url: String, concealed
} }
} }
|> deliverOnMainQueue).start(next: { result in |> deliverOnMainQueue).start(next: { result in
progressDisposable.dispose()
openResolved(result) openResolved(result)
})) }))
} }

View File

@ -606,6 +606,16 @@ public final class AccountViewTracker {
var maxMessageId: MessageId? var maxMessageId: MessageId?
} }
func applyMaxReadIncomingMessageIdForReplyInfo(id: MessageId, maxReadIncomingMessageId: MessageId) {
self.queue.async {
if var state = self.updatedViewCountMessageIdsAndTimestamps[id], var result = state.result {
result.maxReadIncomingMessageId = maxReadIncomingMessageId
state.result = result
self.updatedViewCountMessageIdsAndTimestamps[id] = state
}
}
}
public func replyInfoForMessageId(_ id: MessageId) -> Signal<UpdatedMessageReplyInfo?, NoError> { public func replyInfoForMessageId(_ id: MessageId) -> Signal<UpdatedMessageReplyInfo?, NoError> {
return Signal { [weak self] subscriber in return Signal { [weak self] subscriber in
let state = self?.updatedViewCountMessageIdsAndTimestamps[id] let state = self?.updatedViewCountMessageIdsAndTimestamps[id]
@ -1167,7 +1177,7 @@ public final class AccountViewTracker {
} }
} }
func polledChannel(peerId: PeerId) -> Signal<Void, NoError> { public func polledChannel(peerId: PeerId) -> Signal<Void, NoError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.queue.async { self.queue.async {

View File

@ -4,6 +4,14 @@ import Postbox
import SwiftSignalKit import SwiftSignalKit
import TelegramApi import TelegramApi
private struct DiscussionMessage {
public var messageId: MessageId
public var isChannelPost: Bool
public var maxMessage: MessageId?
public var maxReadIncomingMessageId: MessageId?
public var maxReadOutgoingMessageId: MessageId?
}
private class ReplyThreadHistoryContextImpl { private class ReplyThreadHistoryContextImpl {
private let queue: Queue private let queue: Queue
private let account: Account private let account: Account
@ -41,6 +49,7 @@ private class ReplyThreadHistoryContextImpl {
private var initialStateDisposable: Disposable? private var initialStateDisposable: Disposable?
private var holesDisposable: Disposable? private var holesDisposable: Disposable?
private var readStateDisposable: Disposable? private var readStateDisposable: Disposable?
private var updateInitialStateDisposable: Disposable?
private let readDisposable = MetaDisposable() private let readDisposable = MetaDisposable()
init(queue: Queue, account: Account, data: ChatReplyThreadMessage) { init(queue: Queue, account: Account, data: ChatReplyThreadMessage) {
@ -108,12 +117,107 @@ private class ReplyThreadHistoryContextImpl {
strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud, id: value) strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud, id: value)
} }
}) })
let updateInitialState: Signal<DiscussionMessage, FetchChannelReplyThreadMessageError> = account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(data.messageId.peerId).flatMap(apiInputPeer)
}
|> castError(FetchChannelReplyThreadMessageError.self)
|> mapToSignal { inputPeer -> Signal<DiscussionMessage, FetchChannelReplyThreadMessageError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: data.messageId.id))
|> mapError { _ -> FetchChannelReplyThreadMessageError in
return .generic
}
|> mapToSignal { discussionMessage -> Signal<DiscussionMessage, FetchChannelReplyThreadMessageError> in
return account.postbox.transaction { transaction -> Signal<DiscussionMessage, FetchChannelReplyThreadMessageError> in
switch discussionMessage {
case let .discussionMessage(_, messages, maxId, readInboxMaxId, readOutboxMaxId, chats, users):
let parsedMessages = messages.compactMap { message -> StoreMessage? in
StoreMessage(apiMessage: message)
}
guard let topMessage = parsedMessages.last, let parsedIndex = topMessage.index else {
return .fail(.generic)
}
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel)
}
}
for user in users {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
let _ = transaction.addMessages(parsedMessages, location: .Random)
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
let resolvedMaxMessage: MessageId?
if let maxId = maxId {
resolvedMaxMessage = MessageId(
peerId: parsedIndex.id.peerId,
namespace: Namespaces.Message.Cloud,
id: maxId
)
} else {
resolvedMaxMessage = nil
}
return .single(DiscussionMessage(
messageId: parsedIndex.id,
isChannelPost: true,
maxMessage: resolvedMaxMessage,
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
},
maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
}
))
}
}
|> castError(FetchChannelReplyThreadMessageError.self)
|> switchToLatest
}
}
self.updateInitialStateDisposable = (updateInitialState
|> deliverOnMainQueue).start(next: { [weak self] updatedData in
guard let strongSelf = self else {
return
}
if let maxReadOutgoingMessageId = updatedData.maxReadOutgoingMessageId {
if let current = strongSelf.maxReadOutgoingMessageIdValue {
if maxReadOutgoingMessageId > current {
strongSelf.maxReadOutgoingMessageIdValue = maxReadOutgoingMessageId
}
} else {
strongSelf.maxReadOutgoingMessageIdValue = maxReadOutgoingMessageId
}
}
})
} }
deinit { deinit {
self.initialStateDisposable?.dispose() self.initialStateDisposable?.dispose()
self.holesDisposable?.dispose() self.holesDisposable?.dispose()
self.readDisposable.dispose() self.readDisposable.dispose()
self.updateInitialStateDisposable?.dispose()
} }
func setCurrentHole(entry: MessageHistoryHolesViewEntry?) { func setCurrentHole(entry: MessageHistoryHolesViewEntry?) {
@ -156,6 +260,8 @@ private class ReplyThreadHistoryContextImpl {
for attribute in message.attributes { for attribute in message.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute { if let attribute = attribute as? SourceReferenceMessageAttribute {
if let sourceMessage = transaction.getMessage(attribute.messageId) { if let sourceMessage = transaction.getMessage(attribute.messageId) {
account.viewTracker.applyMaxReadIncomingMessageIdForReplyInfo(id: attribute.messageId, maxReadIncomingMessageId: messageIndex.id)
var updatedAttribute: ReplyThreadMessageAttribute? var updatedAttribute: ReplyThreadMessageAttribute?
for i in 0 ..< sourceMessage.attributes.count { for i in 0 ..< sourceMessage.attributes.count {
if let attribute = sourceMessage.attributes[i] as? ReplyThreadMessageAttribute { if let attribute = sourceMessage.attributes[i] as? ReplyThreadMessageAttribute {
@ -289,7 +395,7 @@ public enum FetchChannelReplyThreadMessageError {
case generic case generic
} }
public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageId) -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> { public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageId, atMessageId: MessageId?) -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
} }
@ -302,14 +408,6 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
let replyInfo = Promise<AccountViewTracker.UpdatedMessageReplyInfo?>() let replyInfo = Promise<AccountViewTracker.UpdatedMessageReplyInfo?>()
replyInfo.set(account.viewTracker.replyInfoForMessageId(messageId)) replyInfo.set(account.viewTracker.replyInfoForMessageId(messageId))
struct DiscussionMessage {
public var messageId: MessageId
public var isChannelPost: Bool
public var maxMessage: MessageId?
public var maxReadIncomingMessageId: MessageId?
public var maxReadOutgoingMessageId: MessageId?
}
let remoteDiscussionMessageSignal: Signal<DiscussionMessage?, NoError> = account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: messageId.id)) let remoteDiscussionMessageSignal: Signal<DiscussionMessage?, NoError> = account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: messageId.id))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.DiscussionMessage?, NoError> in |> `catch` { _ -> Signal<Api.messages.DiscussionMessage?, NoError> in
@ -380,16 +478,12 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
} }
} }
let discussionMessageSignal = (replyInfo.get() let discussionMessageSignal = (replyInfo.get()
|> take(1)
|> mapToSignal { replyInfo -> Signal<DiscussionMessage?, NoError> in |> mapToSignal { replyInfo -> Signal<DiscussionMessage?, NoError> in
guard let replyInfo = replyInfo else { guard let replyInfo = replyInfo else {
return .single(nil) return .single(nil)
} }
return account.postbox.transaction { transaction -> DiscussionMessage? in return account.postbox.transaction { transaction -> DiscussionMessage? in
let isParticipant = transaction.getPeerChatListIndex(replyInfo.commentsPeerId) != nil
guard isParticipant else {
return nil
}
var foundDiscussionMessageId: MessageId? var foundDiscussionMessageId: MessageId?
transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in
for attribute in attributes { for attribute in attributes {
@ -428,7 +522,8 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
discussionMessage.set(discussionMessageSignal) discussionMessage.set(discussionMessageSignal)
let preloadedHistoryPosition: Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> = replyInfo.get() let preloadedHistoryPosition: Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> = replyInfo.get()
|> castError(FetchChannelReplyThreadMessageError.self) |> take(1)
|> castError(FetchChannelReplyThreadMessageError.self)
|> mapToSignal { replyInfo -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in |> mapToSignal { replyInfo -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in
if let replyInfo = replyInfo { if let replyInfo = replyInfo {
return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?) in return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?) in
@ -446,11 +541,12 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
} }
return true return true
}) })
return (threadInput, replyInfo.commentsPeerId, threadMessageId, replyInfo.maxReadIncomingMessageId, replyInfo.maxMessageId) return (threadInput, replyInfo.commentsPeerId, threadMessageId, atMessageId ?? replyInfo.maxReadIncomingMessageId, replyInfo.maxMessageId)
} }
|> castError(FetchChannelReplyThreadMessageError.self) |> castError(FetchChannelReplyThreadMessageError.self)
} else { } else {
return discussionMessage.get() return discussionMessage.get()
|> take(1)
|> castError(FetchChannelReplyThreadMessageError.self) |> castError(FetchChannelReplyThreadMessageError.self)
|> mapToSignal { discussionMessage -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in |> mapToSignal { discussionMessage -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in
guard let discussionMessage = discussionMessage else { guard let discussionMessage = discussionMessage else {
@ -459,7 +555,7 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
let topMessageId = discussionMessage.messageId let topMessageId = discussionMessage.messageId
let commentsPeerId = topMessageId.peerId let commentsPeerId = topMessageId.peerId
return .single((.direct(peerId: commentsPeerId, threadId: makeMessageThreadId(topMessageId)), commentsPeerId, discussionMessage.messageId, discussionMessage.maxReadIncomingMessageId, discussionMessage.maxMessage)) return .single((.direct(peerId: commentsPeerId, threadId: makeMessageThreadId(topMessageId)), commentsPeerId, discussionMessage.messageId, atMessageId ?? discussionMessage.maxReadIncomingMessageId, discussionMessage.maxMessage))
} }
} }
} }
@ -522,6 +618,7 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
return combineLatest( return combineLatest(
discussionMessage.get() discussionMessage.get()
|> take(1)
|> castError(FetchChannelReplyThreadMessageError.self), |> castError(FetchChannelReplyThreadMessageError.self),
preloadedHistory preloadedHistory
) )

View File

@ -539,14 +539,13 @@ func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource,
} }
} }
public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timestamp: Int32) -> Signal<MessageId?, NoError> { public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, threadId: Int64?, timestamp: Int32) -> Signal<MessageId?, NoError> {
return account.postbox.transaction { transaction -> Signal<MessageId?, NoError> in return account.postbox.transaction { transaction -> Signal<MessageId?, NoError> in
if peerId.namespace == Namespaces.Peer.SecretChat { if peerId.namespace == Namespaces.Peer.SecretChat {
return .single(transaction.findClosestMessageIdByTimestamp(peerId: peerId, timestamp: timestamp)) return .single(transaction.findClosestMessageIdByTimestamp(peerId: peerId, timestamp: timestamp))
} else if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { } else if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
var secondaryIndex: Signal<MessageIndex?, NoError> = .single(nil) if let threadId = threadId {
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let migrationReference = cachedData.migrationReference, let secondaryPeer = transaction.getPeer(migrationReference.maxMessageId.peerId), let inputSecondaryPeer = apiInputPeer(secondaryPeer) { let primaryIndex = account.network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: makeThreadIdMessageId(peerId: peerId, threadId: threadId).id, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0))
secondaryIndex = account.network.request(Api.functions.messages.getHistory(peer: inputSecondaryPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0))
|> map { result -> MessageIndex? in |> map { result -> MessageIndex? in
let messages: [Api.Message] let messages: [Api.Message]
switch result { switch result {
@ -569,45 +568,76 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta
|> `catch` { _ -> Signal<MessageIndex?, NoError> in |> `catch` { _ -> Signal<MessageIndex?, NoError> in
return .single(nil) return .single(nil)
} }
} return primaryIndex
let primaryIndex = account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0)) |> map { primaryIndex -> MessageId? in
|> map { result -> MessageIndex? in return primaryIndex?.id
let messages: [Api.Message]
switch result {
case let .messages(apiMessages, _, _):
messages = apiMessages
case let .channelMessages(_, _, _, apiMessages, _, _):
messages = apiMessages
case let .messagesSlice(_, _, _, apiMessages, _, _):
messages = apiMessages
case .messagesNotModified:
messages = []
} }
for message in messages { } else {
if let message = StoreMessage(apiMessage: message) { var secondaryIndex: Signal<MessageIndex?, NoError> = .single(nil)
return message.index if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let migrationReference = cachedData.migrationReference, let secondaryPeer = transaction.getPeer(migrationReference.maxMessageId.peerId), let inputSecondaryPeer = apiInputPeer(secondaryPeer) {
secondaryIndex = account.network.request(Api.functions.messages.getHistory(peer: inputSecondaryPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0))
|> map { result -> MessageIndex? in
let messages: [Api.Message]
switch result {
case let .messages(apiMessages, _, _):
messages = apiMessages
case let .channelMessages(_, _, _, apiMessages, _, _):
messages = apiMessages
case let .messagesSlice(_, _, _, apiMessages, _, _):
messages = apiMessages
case .messagesNotModified:
messages = []
}
for message in messages {
if let message = StoreMessage(apiMessage: message) {
return message.index
}
}
return nil
}
|> `catch` { _ -> Signal<MessageIndex?, NoError> in
return .single(nil)
} }
} }
return nil let primaryIndex = account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0))
} |> map { result -> MessageIndex? in
|> `catch` { _ -> Signal<MessageIndex?, NoError> in let messages: [Api.Message]
return .single(nil) switch result {
} case let .messages(apiMessages, _, _):
return combineLatest(primaryIndex, secondaryIndex) messages = apiMessages
|> map { primaryIndex, secondaryIndex -> MessageId? in case let .channelMessages(_, _, _, apiMessages, _, _):
if let primaryIndex = primaryIndex, let secondaryIndex = secondaryIndex { messages = apiMessages
if abs(primaryIndex.timestamp - timestamp) < abs(secondaryIndex.timestamp - timestamp) { case let .messagesSlice(_, _, _, apiMessages, _, _):
return primaryIndex.id messages = apiMessages
} else { case .messagesNotModified:
return secondaryIndex.id messages = []
}
for message in messages {
if let message = StoreMessage(apiMessage: message) {
return message.index
}
} }
} else if let primaryIndex = primaryIndex {
return primaryIndex.id
} else if let secondaryIndex = secondaryIndex {
return secondaryIndex.id
} else {
return nil return nil
} }
|> `catch` { _ -> Signal<MessageIndex?, NoError> in
return .single(nil)
}
return combineLatest(primaryIndex, secondaryIndex)
|> map { primaryIndex, secondaryIndex -> MessageId? in
if let primaryIndex = primaryIndex, let secondaryIndex = secondaryIndex {
if abs(primaryIndex.timestamp - timestamp) < abs(secondaryIndex.timestamp - timestamp) {
return primaryIndex.id
} else {
return secondaryIndex.id
}
} else if let primaryIndex = primaryIndex {
return primaryIndex.id
} else if let secondaryIndex = secondaryIndex {
return secondaryIndex.id
} else {
return nil
}
}
} }
} else { } else {
return .single(nil) return .single(nil)

View File

@ -2608,7 +2608,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if strongSelf.preloadHistoryPeerId != peerDiscussionId { if strongSelf.preloadHistoryPeerId != peerDiscussionId {
strongSelf.preloadHistoryPeerId = peerDiscussionId strongSelf.preloadHistoryPeerId = peerDiscussionId
if let peerDiscussionId = peerDiscussionId { if let peerDiscussionId = peerDiscussionId {
strongSelf.preloadHistoryPeerIdDisposable.set(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: peerDiscussionId)) let combinedDisposable = DisposableSet()
strongSelf.preloadHistoryPeerIdDisposable.set(combinedDisposable)
combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: peerDiscussionId).start())
combinedDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: peerDiscussionId))
} else { } else {
strongSelf.preloadHistoryPeerIdDisposable.set(nil) strongSelf.preloadHistoryPeerIdDisposable.set(nil)
} }
@ -4257,7 +4260,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}, openCalendarSearch: { [weak self] in }, openCalendarSearch: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
let peerId = strongSelf.chatLocation.peerId
strongSelf.chatDisplayNode.dismissInput() strongSelf.chatDisplayNode.dismissInput()
let controller = ChatDateSelectionSheet(presentationData: strongSelf.presentationData, completion: { timestamp in let controller = ChatDateSelectionSheet(presentationData: strongSelf.presentationData, completion: { timestamp in
@ -4265,7 +4267,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
strongSelf.loadingMessage.set(true) strongSelf.loadingMessage.set(true)
strongSelf.messageIndexDisposable.set((searchMessageIdByTimestamp(account: strongSelf.context.account, peerId: peerId, timestamp: timestamp) |> deliverOnMainQueue).start(next: { messageId in
let peerId: PeerId
let threadId: Int64?
switch strongSelf.chatLocation {
case let .peer(peerIdValue):
peerId = peerIdValue
threadId = nil
case let .replyThread(replyThreadMessage):
peerId = replyThreadMessage.messageId.peerId
threadId = makeMessageThreadId(replyThreadMessage.messageId)
}
strongSelf.messageIndexDisposable.set((searchMessageIdByTimestamp(account: strongSelf.context.account, peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).start(next: { messageId in
if let strongSelf = self { if let strongSelf = self {
strongSelf.loadingMessage.set(false) strongSelf.loadingMessage.set(false)
if let messageId = messageId { if let messageId = messageId {
@ -5089,6 +5103,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
unarchiveAutomaticallyArchivedPeer(account: strongSelf.context.account, peerId: peerId) unarchiveAutomaticallyArchivedPeer(account: strongSelf.context.account, peerId: peerId)
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .succeed(text: strongSelf.presentationData.strings.Conversation_UnarchiveDone), elevatedLayout: false, action: { _ in return false }), in: .current) strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .succeed(text: strongSelf.presentationData.strings.Conversation_UnarchiveDone), elevatedLayout: false, action: { _ in return false }), in: .current)
}, scrollToTop: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.chatDisplayNode.historyNode.scrollToStartOfHistory()
}, viewReplies: { [weak self] sourceMessageId, replyThreadResult in }, viewReplies: { [weak self] sourceMessageId, replyThreadResult in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -8202,7 +8222,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
progressDisposable.dispose() progressDisposable.dispose()
} }
} }
|> deliverOnMainQueue).start(next: { [weak self] index in |> deliverOnMainQueue).start(next: { index in
if index.1 { if index.1 {
if !progressStarted { if !progressStarted {
progressStarted = true progressStarted = true
@ -8223,6 +8243,70 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
func scrollToStartOfHistory() {
let locationInput = ChatHistoryLocationInput(content: .Scroll(index: .lowerBound, anchorIndex: .lowerBound, sourceIndex: .upperBound, scrollPosition: .bottom(0.0), animated: true), id: 0)
let historyView = preloadedChatHistoryViewForLocation(locationInput, context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: [])
let signal = historyView
|> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in
switch historyView {
case .Loading:
return .single((nil, true))
case .HistoryView:
return .single((nil, false))
}
}
|> take(until: { index in
return SignalTakeAction(passthrough: true, complete: !index.1)
})
var cancelImpl: (() -> Void)?
let presentationData = self.presentationData
let displayTime = CACurrentMediaTime()
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
if CACurrentMediaTime() - displayTime > 1.5 {
cancelImpl?()
}
}))
self?.present(controller, in: .window(.root))
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.05, queue: Queue.mainQueue())
let progressDisposable = MetaDisposable()
var progressStarted = false
self.messageIndexDisposable.set((signal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
|> deliverOnMainQueue).start(next: { index in
if index.1 {
if !progressStarted {
progressStarted = true
progressDisposable.set(progressSignal.start())
}
}
}, completed: { [weak self] in
if let strongSelf = self {
strongSelf.loadingMessage.set(false)
strongSelf.chatDisplayNode.historyNode.scrollToStartOfHistory()
}
}))
cancelImpl = { [weak self] in
if let strongSelf = self {
strongSelf.loadingMessage.set(false)
strongSelf.messageIndexDisposable.set(nil)
}
}
}
func updateDownButtonVisibility() { func updateDownButtonVisibility() {
let recordingMediaMessage = self.audioRecorderValue != nil || self.videoRecorderValue != nil let recordingMediaMessage = self.audioRecorderValue != nil || self.videoRecorderValue != nil
self.chatDisplayNode.navigateButtons.displayDownButton = self.shouldDisplayDownButton && !recordingMediaMessage self.chatDisplayNode.navigateButtons.displayDownButton = self.shouldDisplayDownButton && !recordingMediaMessage
@ -8280,13 +8364,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
static func openMessageReplies(context: AccountContext, navigationController: NavigationController, present: @escaping (ViewController, Any?) -> Void, messageId: MessageId, isChannelPost: Bool, atMessage atMessageId: MessageId?, displayModalProgress: Bool) -> Signal<Never, NoError> { static func openMessageReplies(context: AccountContext, navigationController: NavigationController, present: @escaping (ViewController, Any?) -> Void, messageId: MessageId, isChannelPost: Bool, atMessage atMessageId: MessageId?, displayModalProgress: Bool) -> Signal<Never, NoError> {
return Signal { subscriber in return Signal { subscriber in
let foundIndex = Promise<ReplyThreadInfo?>()
foundIndex.set(fetchAndPreloadReplyThreadInfo(context: context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId))
|> map(Optional.init)
|> `catch` { _ -> Signal<ReplyThreadInfo?, NoError> in
return .single(nil)
})
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var cancelImpl: (() -> Void)? var cancelImpl: (() -> Void)?
@ -8298,26 +8375,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
present(statusController, nil) present(statusController, nil)
} }
let disposable = (foundIndex.get() let disposable = (fetchAndPreloadReplyThreadInfo(context: context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId), atMessageId: atMessageId)
|> take(1)
|> deliverOnMainQueue).start(next: { [weak statusController] result in |> deliverOnMainQueue).start(next: { [weak statusController] result in
if displayModalProgress { if displayModalProgress {
statusController?.dismiss() statusController?.dismiss()
} }
if let result = result { let chatLocation: ChatLocation = .replyThread(result.message)
let chatLocation: ChatLocation = .replyThread(result.message)
let subject: ChatControllerSubject?
let subject: ChatControllerSubject? if let atMessageId = atMessageId {
if let atMessageId = atMessageId { subject = .message(atMessageId)
subject = .message(atMessageId) } else {
} else { subject = nil
subject = nil
}
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, subject: subject, activateInput: result.isEmpty, keepStack: .always))
subscriber.putCompletion()
} }
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, subject: subject, activateInput: result.isEmpty, keepStack: .always))
subscriber.putCompletion()
}, error: { _ in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}) })
cancelImpl = { [weak statusController] in cancelImpl = { [weak statusController] in

View File

@ -302,24 +302,34 @@ enum ReplyThreadSubject {
case groupMessage(MessageId) case groupMessage(MessageId)
} }
func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject) -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> { func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject, atMessageId: MessageId?) -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> {
let message: Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> let message: Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError>
switch subject { switch subject {
case let .channelPost(messageId): case let .channelPost(messageId):
message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId) message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId, atMessageId: atMessageId)
case let .groupMessage(messageId): case let .groupMessage(messageId):
message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId) message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId, atMessageId: atMessageId)
} }
return message return message
|> mapToSignal { replyThreadMessage -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> in |> mapToSignal { replyThreadMessage -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> in
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil) let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let preloadSignal = preloadedChatHistoryViewForLocation( let input: ChatHistoryLocationInput
ChatHistoryLocationInput( if let atMessageId = atMessageId {
content: .Initial(count: 60), input = ChatHistoryLocationInput(
content: .InitialSearch(location: .id(atMessageId), count: 30),
id: 0 id: 0
), )
} else {
input = ChatHistoryLocationInput(
content: .Initial(count: 30),
id: 0
)
}
let preloadSignal = preloadedChatHistoryViewForLocation(
input,
context: context, context: context,
chatLocation: .replyThread(replyThreadMessage), chatLocation: .replyThread(replyThreadMessage),
chatLocationContextHolder: chatLocationContextHolder, chatLocationContextHolder: chatLocationContextHolder,

View File

@ -615,7 +615,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
}, action: { c, _ in }, action: { c, _ in
let foundIndex = Promise<ChatReplyThreadMessage?>() let foundIndex = Promise<ChatReplyThreadMessage?>()
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel { if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel {
foundIndex.set(fetchChannelReplyThreadMessage(account: context.account, messageId: messages[0].id) foundIndex.set(fetchChannelReplyThreadMessage(account: context.account, messageId: messages[0].id, atMessageId: nil)
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in |> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
return .single(nil) return .single(nil)

View File

@ -676,7 +676,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
edited = true edited = true
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -271,7 +271,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
self.addSubnode(self.statusNode) self.addSubnode(self.statusNode)
} }
func asyncLayout() -> (_ presentationData: ChatPresentationData, _ automaticDownloadSettings: MediaAutoDownloadSettings, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ context: AccountContext, _ controllerInteraction: ChatControllerInteraction, _ message: Message, _ messageRead: Bool, _ title: String?, _ subtitle: NSAttributedString?, _ text: String?, _ entities: [MessageTextEntity]?, _ media: (Media, ChatMessageAttachedContentNodeMediaFlags)?, _ mediaBadge: String?, _ actionIcon: ChatMessageAttachedContentActionIcon?, _ actionTitle: String?, _ displayLine: Bool, _ layoutConstants: ChatMessageItemLayoutConstants, _ constrainedSize: CGSize) -> (CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) { func asyncLayout() -> (_ presentationData: ChatPresentationData, _ automaticDownloadSettings: MediaAutoDownloadSettings, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ context: AccountContext, _ controllerInteraction: ChatControllerInteraction, _ message: Message, _ messageRead: Bool, _ chatLocation: ChatLocation, _ title: String?, _ subtitle: NSAttributedString?, _ text: String?, _ entities: [MessageTextEntity]?, _ media: (Media, ChatMessageAttachedContentNodeMediaFlags)?, _ mediaBadge: String?, _ actionIcon: ChatMessageAttachedContentActionIcon?, _ actionTitle: String?, _ displayLine: Bool, _ layoutConstants: ChatMessageItemLayoutConstants, _ constrainedSize: CGSize) -> (CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
let textAsyncLayout = TextNode.asyncLayout(self.textNode) let textAsyncLayout = TextNode.asyncLayout(self.textNode)
let currentImage = self.media as? TelegramMediaImage let currentImage = self.media as? TelegramMediaImage
let imageLayout = self.inlineImageNode.asyncLayout() let imageLayout = self.inlineImageNode.asyncLayout()
@ -284,7 +284,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let currentAdditionalImageBadgeNode = self.additionalImageBadgeNode let currentAdditionalImageBadgeNode = self.additionalImageBadgeNode
return { presentationData, automaticDownloadSettings, associatedData, attributes, context, controllerInteraction, message, messageRead, title, subtitle, text, entities, mediaAndFlags, mediaBadge, actionIcon, actionTitle, displayLine, layoutConstants, constrainedSize in return { presentationData, automaticDownloadSettings, associatedData, attributes, context, controllerInteraction, message, messageRead, chatLocation, title, subtitle, text, entities, mediaAndFlags, mediaBadge, actionIcon, actionTitle, displayLine, layoutConstants, constrainedSize in
let isPreview = presentationData.isPreview let isPreview = presentationData.isPreview
let fontSize: CGFloat = floor(presentationData.fontSize.baseDisplaySize * 15.0 / 17.0) let fontSize: CGFloat = floor(presentationData.fontSize.baseDisplaySize * 15.0 / 17.0)
@ -323,7 +323,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = chatLocation {
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }
@ -424,7 +424,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
refineContentImageLayout = refineLayout refineContentImageLayout = refineLayout
} else if file.isInstantVideo { } else if file.isInstantVideo {
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, presentationData: presentationData, associatedData: associatedData, attributes: attributes), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload) let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, chatLocation: chatLocation, presentationData: presentationData, associatedData: associatedData, attributes: attributes), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload)
initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight
contentInstantVideoSizeAndApply = (videoLayout, apply) contentInstantVideoSizeAndApply = (videoLayout, apply)
} else if file.isVideo { } else if file.isVideo {
@ -474,7 +474,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
} }
} }
let (_, refineLayout) = contentFileLayout(context, presentationData, message, attributes, file, automaticDownload, message.effectivelyIncoming(context.account.peerId), false, associatedData.forcedResourceStatus, statusType, CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)) let (_, refineLayout) = contentFileLayout(context, presentationData, message, chatLocation, attributes, file, automaticDownload, message.effectivelyIncoming(context.account.peerId), false, associatedData.forcedResourceStatus, statusType, CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height))
refineContentFileLayout = refineLayout refineContentFileLayout = refineLayout
} }
} else if let image = media as? TelegramMediaImage { } else if let image = media as? TelegramMediaImage {

View File

@ -98,15 +98,17 @@ final class ChatMessageBubbleContentItem {
let controllerInteraction: ChatControllerInteraction let controllerInteraction: ChatControllerInteraction
let message: Message let message: Message
let read: Bool let read: Bool
let chatLocation: ChatLocation
let presentationData: ChatPresentationData let presentationData: ChatPresentationData
let associatedData: ChatMessageItemAssociatedData let associatedData: ChatMessageItemAssociatedData
let attributes: ChatMessageEntryAttributes let attributes: ChatMessageEntryAttributes
init(context: AccountContext, controllerInteraction: ChatControllerInteraction, message: Message, read: Bool, presentationData: ChatPresentationData, associatedData: ChatMessageItemAssociatedData, attributes: ChatMessageEntryAttributes) { init(context: AccountContext, controllerInteraction: ChatControllerInteraction, message: Message, read: Bool, chatLocation: ChatLocation, presentationData: ChatPresentationData, associatedData: ChatMessageItemAssociatedData, attributes: ChatMessageEntryAttributes) {
self.context = context self.context = context
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
self.message = message self.message = message
self.read = read self.read = read
self.chatLocation = chatLocation
self.presentationData = presentationData self.presentationData = presentationData
self.associatedData = associatedData self.associatedData = associatedData
self.attributes = attributes self.attributes = attributes

View File

@ -906,7 +906,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
var needShareButton = false var needShareButton = false
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
needShareButton = false
allowFullWidth = true
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
needShareButton = false needShareButton = false
} else if item.message.id.peerId == item.context.account.peerId { } else if item.message.id.peerId == item.context.account.peerId {
if let _ = sourceReference { if let _ = sourceReference {
@ -1134,7 +1137,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
prepareContentPosition = .linear(top: topPosition, bottom: refinedBottomPosition) prepareContentPosition = .linear(top: topPosition, bottom: refinedBottomPosition)
} }
let contentItem = ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: message, read: read, presentationData: item.presentationData, associatedData: item.associatedData, attributes: attributes) let contentItem = ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: message, read: read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: attributes)
var itemSelection: Bool? var itemSelection: Bool?
if case .mosaic = prepareContentPosition { if case .mosaic = prepareContentPosition {
@ -1288,7 +1291,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -152,7 +152,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -44,7 +44,7 @@ final class ChatMessageEventLogPreviousDescriptionContentNode: ChatMessageBubble
} }
let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil
let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize) let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)

View File

@ -39,7 +39,7 @@ final class ChatMessageEventLogPreviousLinkContentNode: ChatMessageBubbleContent
let text: String = item.message.text let text: String = item.message.text
let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil
let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize) let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)

View File

@ -44,7 +44,7 @@ final class ChatMessageEventLogPreviousMessageContentNode: ChatMessageBubbleCont
} }
let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil
let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize) let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)

View File

@ -87,7 +87,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
let automaticDownload = shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: selectedFile!) let automaticDownload = shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: selectedFile!)
let (initialWidth, refineLayout) = interactiveFileLayout(item.context, item.presentationData, item.message, item.attributes, selectedFile!, automaticDownload, item.message.effectivelyIncoming(item.context.account.peerId), item.associatedData.isRecentActions, item.associatedData.forcedResourceStatus, statusType, CGSize(width: constrainedSize.width - layoutConstants.file.bubbleInsets.left - layoutConstants.file.bubbleInsets.right, height: constrainedSize.height)) let (initialWidth, refineLayout) = interactiveFileLayout(item.context, item.presentationData, item.message, item.chatLocation, item.attributes, selectedFile!, automaticDownload, item.message.effectivelyIncoming(item.context.account.peerId), item.associatedData.isRecentActions, item.associatedData.forcedResourceStatus, statusType, CGSize(width: constrainedSize.width - layoutConstants.file.bubbleInsets.left - layoutConstants.file.bubbleInsets.right, height: constrainedSize.height))
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)

View File

@ -71,7 +71,7 @@ final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, title, nil, item.message.text.isEmpty ? text : item.message.text, item.message.text.isEmpty ? nil : messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize) let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, .peer(item.message.id.peerId), title, nil, item.message.text.isEmpty ? text : item.message.text, item.message.text.isEmpty ? nil : messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, constrainedSize)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)

View File

@ -276,7 +276,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
} }
} }
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, automaticDownload) let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, automaticDownload)
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: 0.0), size: videoLayout.contentSize) let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: 0.0), size: videoLayout.contentSize)

View File

@ -204,7 +204,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
} }
} }
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void))) { func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void))) {
let currentFile = self.file let currentFile = self.file
let titleAsyncLayout = TextNode.asyncLayout(self.titleNode) let titleAsyncLayout = TextNode.asyncLayout(self.titleNode)
@ -214,7 +214,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let currentMessage = self.message let currentMessage = self.message
return { context, presentationData, message, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, constrainedSize in return { context, presentationData, message, chatLocation, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, constrainedSize in
return (CGFloat.greatestFiniteMagnitude, { constrainedSize in return (CGFloat.greatestFiniteMagnitude, { constrainedSize in
let titleFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 16.0 / 17.0)) let titleFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 16.0 / 17.0))
let descriptionFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0)) let descriptionFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0))
@ -300,7 +300,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = chatLocation {
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }
@ -971,12 +971,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize) self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize)
} }
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> ChatMessageInteractiveFileNode))) { static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> ChatMessageInteractiveFileNode))) {
let currentAsyncLayout = node?.asyncLayout() let currentAsyncLayout = node?.asyncLayout()
return { context, presentationData, message, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, constrainedSize in return { context, presentationData, message, chatLocation, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, constrainedSize in
var fileNode: ChatMessageInteractiveFileNode var fileNode: ChatMessageInteractiveFileNode
var fileLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void))) var fileLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void)))
if let node = node, let currentAsyncLayout = currentAsyncLayout { if let node = node, let currentAsyncLayout = currentAsyncLayout {
fileNode = node fileNode = node
@ -986,7 +986,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
fileLayout = fileNode.asyncLayout() fileLayout = fileNode.asyncLayout()
} }
let (initialWidth, continueLayout) = fileLayout(context, presentationData, message, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, constrainedSize) let (initialWidth, continueLayout) = fileLayout(context, presentationData, message, chatLocation, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, constrainedSize)
return (initialWidth, { constrainedSize in return (initialWidth, { constrainedSize in
let (finalWidth, finalLayout) = continueLayout(constrainedSize) let (finalWidth, finalLayout) = continueLayout(constrainedSize)

View File

@ -257,7 +257,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -74,7 +74,7 @@ final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, automaticDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, title, subtitle, text, nil, mediaAndFlags, nil, nil, nil, false, layoutConstants, constrainedSize) let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, automaticDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, item.chatLocation, title, subtitle, text, nil, mediaAndFlags, nil, nil, nil, false, layoutConstants, constrainedSize)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)

View File

@ -182,7 +182,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -183,7 +183,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
} }
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -1030,7 +1030,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -61,7 +61,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? RestrictedContentMessageAttribute { } else if let attribute = attribute as? RestrictedContentMessageAttribute {
rawText = attribute.platformText(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) ?? "" rawText = attribute.platformText(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) ?? ""
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -360,7 +360,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
edited = true edited = true
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -115,7 +115,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
edited = !attribute.isHidden edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute { } else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute { } else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info { if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count) dateReplies = Int(attribute.count)
} }

View File

@ -294,7 +294,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, title, subtitle, text, entities, mediaAndFlags, badge, actionIcon, actionTitle, true, layoutConstants, constrainedSize) let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, item.chatLocation, title, subtitle, text, entities, mediaAndFlags, badge, actionIcon, actionTitle, true, layoutConstants, constrainedSize)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)

View File

@ -121,6 +121,7 @@ final class ChatPanelInterfaceInteraction {
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
let openPeersNearby: () -> Void let openPeersNearby: () -> Void
let unarchivePeer: () -> Void let unarchivePeer: () -> Void
let scrollToTop: () -> Void
let viewReplies: (MessageId?, ChatReplyThreadMessage) -> Void let viewReplies: (MessageId?, ChatReplyThreadMessage) -> Void
let statuses: ChatPanelInterfaceInteractionStatuses? let statuses: ChatPanelInterfaceInteractionStatuses?
@ -196,6 +197,7 @@ final class ChatPanelInterfaceInteraction {
openPeersNearby: @escaping () -> Void, openPeersNearby: @escaping () -> Void,
displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void,
unarchivePeer: @escaping () -> Void, unarchivePeer: @escaping () -> Void,
scrollToTop: @escaping () -> Void,
viewReplies: @escaping (MessageId?, ChatReplyThreadMessage) -> Void, viewReplies: @escaping (MessageId?, ChatReplyThreadMessage) -> Void,
statuses: ChatPanelInterfaceInteractionStatuses? statuses: ChatPanelInterfaceInteractionStatuses?
) { ) {
@ -270,6 +272,7 @@ final class ChatPanelInterfaceInteraction {
self.openPeersNearby = openPeersNearby self.openPeersNearby = openPeersNearby
self.displaySearchResultsTooltip = displaySearchResultsTooltip self.displaySearchResultsTooltip = displaySearchResultsTooltip
self.unarchivePeer = unarchivePeer self.unarchivePeer = unarchivePeer
self.scrollToTop = scrollToTop
self.viewReplies = viewReplies self.viewReplies = viewReplies
self.statuses = statuses self.statuses = statuses
} }

View File

@ -299,11 +299,12 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
@objc func tapped() { @objc func tapped() {
if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage { if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage {
if self.isReplyThread { if self.isReplyThread {
if let sourceReference = message.sourceReference { interfaceInteraction.scrollToTop()
/*if let sourceReference = message.sourceReference {
interfaceInteraction.navigateToMessage(sourceReference.messageId, true) interfaceInteraction.navigateToMessage(sourceReference.messageId, true)
} else { } else {
interfaceInteraction.navigateToMessage(message.id, false) interfaceInteraction.navigateToMessage(message.id, false)
} }*/
} else { } else {
interfaceInteraction.navigateToMessage(message.id, false) interfaceInteraction.navigateToMessage(message.id, false)
} }

View File

@ -126,7 +126,9 @@ final class ChatRecentActionsController: TelegramBaseController {
}, openScheduledMessages: { }, openScheduledMessages: {
}, openPeersNearby: { }, openPeersNearby: {
}, displaySearchResultsTooltip: { _, _ in }, displaySearchResultsTooltip: { _, _ in
}, unarchivePeer: {}, viewReplies: { _, _ in }, statuses: nil) }, unarchivePeer: {
}, scrollToTop: {
}, viewReplies: { _, _ in }, statuses: nil)
self.navigationItem.titleView = self.titleView self.navigationItem.titleView = self.titleView

View File

@ -190,7 +190,6 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
} }
} }
if case .replyThread = interfaceState.chatLocation { if case .replyThread = interfaceState.chatLocation {
self.calendarButton.isHidden = true
canSearchMembers = false canSearchMembers = false
} }
self.membersButton.isHidden = (!(interfaceState.search?.query.isEmpty ?? true)) || self.displayActivity || !canSearchMembers self.membersButton.isHidden = (!(interfaceState.search?.query.isEmpty ?? true)) || self.displayActivity || !canSearchMembers

View File

@ -92,9 +92,9 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId), peekData: nil)) openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId), peekData: nil))
case let .replyThreadMessage(replyThreadMessage, messageId): case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = navigationController { if let navigationController = navigationController {
ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in let _ = ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in
present(c, a) present(c, a)
}, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true) }, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true).start()
} }
case let .stickerPack(name): case let .stickerPack(name):
dismissInput() dismissInput()

View File

@ -431,7 +431,9 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, openScheduledMessages: { }, openScheduledMessages: {
}, openPeersNearby: { }, openPeersNearby: {
}, displaySearchResultsTooltip: { _, _ in }, displaySearchResultsTooltip: { _, _ in
}, unarchivePeer: {}, viewReplies: { _, _ in }, statuses: nil) }, unarchivePeer: {
}, scrollToTop: {
}, viewReplies: { _, _ in }, statuses: nil)
self.selectionPanel.interfaceInteraction = interfaceInteraction self.selectionPanel.interfaceInteraction = interfaceInteraction

View File

@ -61,9 +61,9 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
} }
case let .replyThreadMessage(replyThreadMessage, messageId): case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = controller.navigationController as? NavigationController { if let navigationController = controller.navigationController as? NavigationController {
ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { [weak controller] c, a in let _ = ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a) controller?.present(c, in: .window(.root), with: a)
}, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true) }, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true).start()
} }
case let .stickerPack(name): case let .stickerPack(name):
let packReference: StickerPackReference = .name(name) let packReference: StickerPackReference = .name(name)

View File

@ -321,7 +321,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
return .single(.channelMessage(peerId: peer.id, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id))) return .single(.channelMessage(peerId: peer.id, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)))
case let .replyThread(id, replyId): case let .replyThread(id, replyId):
let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id) let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)
return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId) return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId, atMessageId: nil)
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in |> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
return .single(nil) return .single(nil)
@ -371,7 +371,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
if let foundPeer = foundPeer { if let foundPeer = foundPeer {
if let threadId = threadId { if let threadId = threadId {
let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId) let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId)
return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId) return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId, atMessageId: nil)
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in |> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
return .single(nil) return .single(nil)