mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Comments update
This commit is contained in:
parent
d8ef15ee00
commit
95b12456dd
@ -170,7 +170,7 @@ public enum ResolvedUrl {
|
||||
case botStart(peerId: PeerId, payload: String)
|
||||
case groupBotStart(peerId: PeerId, payload: String)
|
||||
case channelMessage(peerId: PeerId, messageId: MessageId)
|
||||
case replyThreadMessage(replyThreadMessageId: MessageId, isChannelPost: Bool, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, messageId: MessageId)
|
||||
case replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage, messageId: MessageId)
|
||||
case stickerPack(name: String)
|
||||
case instantView(TelegramMediaWebpage, String?)
|
||||
case proxy(host: String, port: Int32, username: String?, password: String?, secret: Data?)
|
||||
@ -253,7 +253,7 @@ public enum ChatSearchDomain: Equatable {
|
||||
|
||||
public enum ChatLocation: Equatable {
|
||||
case peer(PeerId)
|
||||
case replyThread(threadMessageId: MessageId, isChannelPost: Bool, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?)
|
||||
case replyThread(ChatReplyThreadMessage)
|
||||
}
|
||||
|
||||
public final class NavigateToChatControllerParams {
|
||||
|
@ -230,7 +230,7 @@ static int32_t fixedTimeDifferenceValue = 0;
|
||||
#if DEBUG
|
||||
_tempKeyExpiration = 1 * 60 * 60;
|
||||
#else
|
||||
_tempKeyExpiration = 1 * 60 * 60;
|
||||
_tempKeyExpiration = 24 * 60 * 60;
|
||||
#endif
|
||||
|
||||
_datacenterSeedAddressSetById = [[NSMutableDictionary alloc] init];
|
||||
|
@ -6,7 +6,7 @@ public enum ChatLocationInput {
|
||||
case external(PeerId, Signal<MessageHistoryViewExternalInput, NoError>)
|
||||
}
|
||||
|
||||
enum ResolvedChatLocationInput {
|
||||
public enum ResolvedChatLocationInput {
|
||||
case peer(PeerId)
|
||||
case external(MessageHistoryViewExternalInput)
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ final class MessageHistoryThreadHoleIndexTable: Table {
|
||||
let key = ValueBoxKey(length: 8 + 8 + 4 + 4)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt64(8, value: threadId)
|
||||
key.setInt32(8 + 4, value: namespace)
|
||||
key.setInt32(8 + 8, value: namespace)
|
||||
let tagValue: UInt32
|
||||
switch space {
|
||||
case .everywhere:
|
||||
|
@ -68,6 +68,11 @@ public final class Transaction {
|
||||
return self.postbox?.messageHistoryHoleIndexTable.containing(id: id) ?? [:]
|
||||
}
|
||||
|
||||
public func getHoles(peerId: PeerId, namespace: MessageId.Namespace) -> IndexSet {
|
||||
assert(!self.disposed)
|
||||
return self.postbox?.messageHistoryHoleIndexTable.closest(peerId: peerId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1)) ?? IndexSet()
|
||||
}
|
||||
|
||||
public func addThreadIndexHole(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.addThreadIndexHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range)
|
||||
@ -985,6 +990,26 @@ public final class Transaction {
|
||||
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f)
|
||||
}
|
||||
|
||||
public func getMessagesHistoryViewState(input: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, namespaces: MessageIdNamespaces) -> MessageHistoryView {
|
||||
precondition(!self.disposed)
|
||||
guard let postbox = self.postbox else {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
precondition(postbox.queue.isCurrent())
|
||||
|
||||
var view: MessageHistoryView?
|
||||
|
||||
let subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = Subscriber(next: { next in
|
||||
view = next.0
|
||||
}, error: { _ in }, completed: {})
|
||||
|
||||
let disposable = postbox.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: input, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: nil, namespaces: namespaces, orderStatistics: MessageHistoryViewOrderStatistics(), additionalData: [])
|
||||
disposable.dispose()
|
||||
|
||||
return view!
|
||||
}
|
||||
|
||||
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.invalidateMessageHistoryTagsSummary(peerId: peerId, namespace: namespace, tagMask: tagMask)
|
||||
@ -2530,7 +2555,7 @@ public final class Postbox {
|
||||
}
|
||||
}
|
||||
|
||||
private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable {
|
||||
fileprivate func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable {
|
||||
var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:]
|
||||
var mainPeerIdForTopTaggedMessages: PeerId?
|
||||
switch peerIds {
|
||||
|
@ -2291,21 +2291,21 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
|
||||
final class MessageThreadStatsRecord {
|
||||
var count: Int = 0
|
||||
var peers: [PeerId] = []
|
||||
var peers: [ReplyThreadUserMessage] = []
|
||||
}
|
||||
var messageThreadStatsDifferences: [MessageId: MessageThreadStatsRecord] = [:]
|
||||
func addMessageThreadStatsDifference(threadMessageId: MessageId, add: Int, remove: Int, addedMessagePeer: PeerId?) {
|
||||
func addMessageThreadStatsDifference(threadMessageId: MessageId, add: Int, remove: Int, addedMessagePeer: PeerId?, addedMessageId: MessageId?, isOutgoing: Bool) {
|
||||
if let value = messageThreadStatsDifferences[threadMessageId] {
|
||||
value.count += add - remove
|
||||
if let addedMessagePeer = addedMessagePeer {
|
||||
value.peers.append(addedMessagePeer)
|
||||
if let addedMessagePeer = addedMessagePeer, let addedMessageId = addedMessageId {
|
||||
value.peers.append(ReplyThreadUserMessage(id: addedMessagePeer, messageId: addedMessageId, isOutgoing: isOutgoing))
|
||||
}
|
||||
} else {
|
||||
let value = MessageThreadStatsRecord()
|
||||
messageThreadStatsDifferences[threadMessageId] = value
|
||||
value.count = add - remove
|
||||
if let addedMessagePeer = addedMessagePeer {
|
||||
value.peers.append(addedMessagePeer)
|
||||
if let addedMessagePeer = addedMessagePeer, let addedMessageId = addedMessageId {
|
||||
value.peers.append(ReplyThreadUserMessage(id: addedMessagePeer, messageId: addedMessageId, isOutgoing: isOutgoing))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2320,7 +2320,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
let messageThreadId = makeThreadIdMessageId(peerId: message.id.peerId, threadId: threadId)
|
||||
if id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if !transaction.messageExists(id: id) {
|
||||
addMessageThreadStatsDifference(threadMessageId: messageThreadId, add: 1, remove: 0, addedMessagePeer: message.authorId)
|
||||
addMessageThreadStatsDifference(threadMessageId: messageThreadId, add: 1, remove: 0, addedMessagePeer: message.authorId, addedMessageId: id, isOutgoing: !message.flags.contains(.Incoming))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2338,6 +2338,13 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
} else {
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)]![authorId] = activityValue
|
||||
}
|
||||
if let threadId = message.threadId {
|
||||
if updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: threadId)] == nil {
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: threadId)] = [authorId: activityValue]
|
||||
} else {
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: threadId)]![authorId] = activityValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if case let .Id(id) = message.id {
|
||||
@ -2435,7 +2442,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
}
|
||||
case let .DeleteMessages(ids):
|
||||
deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, manualAddMessageThreadStatsDifference: { id, add, remove in
|
||||
addMessageThreadStatsDifference(threadMessageId: id, add: add, remove: remove, addedMessagePeer: nil)
|
||||
addMessageThreadStatsDifference(threadMessageId: id, add: add, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false)
|
||||
})
|
||||
case let .UpdateMinAvailableMessage(id):
|
||||
if let message = transaction.getMessage(id) {
|
||||
|
@ -241,8 +241,15 @@ private final class FeaturedStickerPacksContext {
|
||||
}
|
||||
|
||||
private struct ViewCountContextState {
|
||||
struct ReplyInfo {
|
||||
var commentsPeerId: PeerId?
|
||||
var maxReadIncomingMessageId: MessageId?
|
||||
var maxMessageId: MessageId?
|
||||
}
|
||||
|
||||
var timestamp: Int32
|
||||
var clientId: Int32
|
||||
var result: ReplyInfo?
|
||||
|
||||
func isStillValidFor(_ other: ViewCountContextState) -> Bool {
|
||||
if other.timestamp > self.timestamp + 30 {
|
||||
@ -592,10 +599,32 @@ public final class AccountViewTracker {
|
||||
}
|
||||
}
|
||||
|
||||
public struct UpdatedMessageReplyInfo {
|
||||
var timestamp: Int32
|
||||
var commentsPeerId: PeerId
|
||||
var maxReadIncomingMessageId: MessageId?
|
||||
var maxMessageId: MessageId?
|
||||
}
|
||||
|
||||
public func replyInfoForMessageId(_ id: MessageId) -> Signal<UpdatedMessageReplyInfo?, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
let state = self?.updatedViewCountMessageIdsAndTimestamps[id]
|
||||
let result = state?.result
|
||||
if let state = state, let result = result, let commentsPeerId = result.commentsPeerId {
|
||||
subscriber.putNext(UpdatedMessageReplyInfo(timestamp: state.timestamp, commentsPeerId: commentsPeerId, maxReadIncomingMessageId: result.maxReadIncomingMessageId, maxMessageId: result.maxMessageId))
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
return EmptyDisposable
|
||||
}
|
||||
|> runOn(self.queue)
|
||||
}
|
||||
|
||||
public func updateViewCountForMessageIds(messageIds: Set<MessageId>, clientId: Int32) {
|
||||
self.queue.async {
|
||||
var addedMessageIds: [MessageId] = []
|
||||
let updatedState = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: clientId)
|
||||
let updatedState = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: clientId, result: nil)
|
||||
for messageId in messageIds {
|
||||
let messageTimestamp = self.updatedViewCountMessageIdsAndTimestamps[messageId]
|
||||
if messageTimestamp == nil || !messageTimestamp!.isStillValidFor(updatedState) {
|
||||
@ -609,106 +638,128 @@ public final class AccountViewTracker {
|
||||
self.nextUpdatedViewCountDisposableId += 1
|
||||
|
||||
if let account = self.account {
|
||||
let signal = (account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return account.network.request(Api.functions.messages.getMessagesViews(peer: inputPeer, id: messageIds.map { $0.id }, increment: .boolTrue))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.MessageViews?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
guard case let .messageViews(viewCounts, chats, users)? = result else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
|
||||
for apiUser in users {
|
||||
if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) {
|
||||
peers.append(user)
|
||||
if let presence = TelegramUserPresence(apiUser: apiUser) {
|
||||
peerPresences[user.id] = presence
|
||||
}
|
||||
}
|
||||
}
|
||||
for chat in chats {
|
||||
if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
for i in 0 ..< messageIds.count {
|
||||
if i < viewCounts.count {
|
||||
if case let .messageViews(_, views, forwards, replies) = viewCounts[i] {
|
||||
transaction.updateMessage(messageIds[i], update: { currentMessage in
|
||||
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
|
||||
var attributes = currentMessage.attributes
|
||||
var foundReplies = false
|
||||
var commentsChannelId: PeerId?
|
||||
var recentRepliersPeerIds: [PeerId]?
|
||||
var repliesCount: Int32?
|
||||
var repliesMaxId: Int32?
|
||||
var repliesReadMaxId: Int32?
|
||||
if let replies = replies {
|
||||
switch replies {
|
||||
case let .messageReplies(_, repliesCountValue, _, recentRepliers, channelId, maxId, readMaxId):
|
||||
if let channelId = channelId {
|
||||
commentsChannelId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
}
|
||||
repliesCount = repliesCountValue
|
||||
if let recentRepliers = recentRepliers {
|
||||
recentRepliersPeerIds = recentRepliers.map { $0.peerId }
|
||||
} else {
|
||||
recentRepliersPeerIds = nil
|
||||
}
|
||||
repliesMaxId = maxId
|
||||
repliesReadMaxId = readMaxId
|
||||
}
|
||||
}
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ViewCountMessageAttribute {
|
||||
if let views = views {
|
||||
attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(views)))
|
||||
}
|
||||
} else if let _ = attributes[j] as? ForwardCountMessageAttribute {
|
||||
if let forwards = forwards {
|
||||
attributes[j] = ForwardCountMessageAttribute(count: Int(forwards))
|
||||
}
|
||||
} else if let _ = attributes[j] as? ReplyThreadMessageAttribute {
|
||||
foundReplies = true
|
||||
if let repliesCount = repliesCount {
|
||||
attributes[j] = ReplyThreadMessageAttribute(count: repliesCount, latestUsers: recentRepliersPeerIds ?? [], commentsPeerId: commentsChannelId, maxMessageId: repliesMaxId, maxReadMessageId: repliesReadMaxId)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !foundReplies, let repliesCount = repliesCount {
|
||||
attributes.append(ReplyThreadMessageAttribute(count: repliesCount, latestUsers: recentRepliersPeerIds ?? [], commentsPeerId: commentsChannelId, maxMessageId: repliesMaxId, maxReadMessageId: repliesReadMaxId))
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let signal: Signal<[MessageId: ViewCountContextState], NoError> = (account.postbox.transaction { transaction -> Signal<[MessageId: ViewCountContextState], NoError> in
|
||||
guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
|
||||
return .complete()
|
||||
}
|
||||
} |> switchToLatest)
|
||||
return account.network.request(Api.functions.messages.getMessagesViews(peer: inputPeer, id: messageIds.map { $0.id }, increment: .boolTrue))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.MessageViews?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<[MessageId: ViewCountContextState], NoError> in
|
||||
guard case let .messageViews(viewCounts, chats, users)? = result else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> [MessageId: ViewCountContextState] in
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
|
||||
var resultStates: [MessageId: ViewCountContextState] = [:]
|
||||
|
||||
for apiUser in users {
|
||||
if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) {
|
||||
peers.append(user)
|
||||
if let presence = TelegramUserPresence(apiUser: apiUser) {
|
||||
peerPresences[user.id] = presence
|
||||
}
|
||||
}
|
||||
}
|
||||
for chat in chats {
|
||||
if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
for i in 0 ..< messageIds.count {
|
||||
if i < viewCounts.count {
|
||||
if case let .messageViews(_, views, forwards, replies) = viewCounts[i] {
|
||||
transaction.updateMessage(messageIds[i], update: { currentMessage in
|
||||
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
|
||||
var attributes = currentMessage.attributes
|
||||
var foundReplies = false
|
||||
var commentsChannelId: PeerId?
|
||||
var recentRepliersPeerIds: [PeerId]?
|
||||
var repliesCount: Int32?
|
||||
var repliesMaxId: Int32?
|
||||
var repliesReadMaxId: Int32?
|
||||
if let replies = replies {
|
||||
switch replies {
|
||||
case let .messageReplies(_, repliesCountValue, _, recentRepliers, channelId, maxId, readMaxId):
|
||||
if let channelId = channelId {
|
||||
commentsChannelId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
}
|
||||
repliesCount = repliesCountValue
|
||||
if let recentRepliers = recentRepliers {
|
||||
recentRepliersPeerIds = recentRepliers.map { $0.peerId }
|
||||
} else {
|
||||
recentRepliersPeerIds = nil
|
||||
}
|
||||
repliesMaxId = maxId
|
||||
repliesReadMaxId = readMaxId
|
||||
}
|
||||
}
|
||||
var maxReadIncomingMessageId: MessageId?
|
||||
var maxMessageId: MessageId?
|
||||
if let commentsChannelId = commentsChannelId {
|
||||
if let repliesReadMaxId = repliesReadMaxId {
|
||||
maxReadIncomingMessageId = MessageId(peerId: commentsChannelId, namespace: Namespaces.Message.Cloud, id: repliesReadMaxId)
|
||||
}
|
||||
if let repliesMaxId = repliesMaxId {
|
||||
maxMessageId = MessageId(peerId: commentsChannelId, namespace: Namespaces.Message.Cloud, id: repliesMaxId)
|
||||
}
|
||||
}
|
||||
resultStates[messageIds[i]] = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: clientId, result: ViewCountContextState.ReplyInfo(commentsPeerId: commentsChannelId, maxReadIncomingMessageId: maxReadIncomingMessageId, maxMessageId: maxMessageId))
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ViewCountMessageAttribute {
|
||||
if let views = views {
|
||||
attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(views)))
|
||||
}
|
||||
} else if let _ = attributes[j] as? ForwardCountMessageAttribute {
|
||||
if let forwards = forwards {
|
||||
attributes[j] = ForwardCountMessageAttribute(count: Int(forwards))
|
||||
}
|
||||
} else if let _ = attributes[j] as? ReplyThreadMessageAttribute {
|
||||
foundReplies = true
|
||||
if let repliesCount = repliesCount {
|
||||
attributes[j] = ReplyThreadMessageAttribute(count: repliesCount, latestUsers: recentRepliersPeerIds ?? [], commentsPeerId: commentsChannelId, maxMessageId: repliesMaxId, maxReadMessageId: repliesReadMaxId)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !foundReplies, let repliesCount = repliesCount {
|
||||
attributes.append(ReplyThreadMessageAttribute(count: repliesCount, latestUsers: recentRepliersPeerIds ?? [], commentsPeerId: commentsChannelId, maxMessageId: repliesMaxId, maxReadMessageId: repliesReadMaxId))
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultStates
|
||||
}
|
||||
}
|
||||
}
|
||||
|> switchToLatest)
|
||||
|> afterDisposed { [weak self] in
|
||||
self?.queue.async {
|
||||
self?.updatedViewCountDisposables.set(nil, forKey: disposableId)
|
||||
}
|
||||
}
|
||||
self.updatedViewCountDisposables.set(signal.start(), forKey: disposableId)
|
||||
|> deliverOn(self.queue)
|
||||
self.updatedViewCountDisposables.set(signal.start(next: { [weak self] updatedStates in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
for (id, state) in updatedStates {
|
||||
strongSelf.updatedViewCountMessageIdsAndTimestamps[id] = state
|
||||
}
|
||||
}), forKey: disposableId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,9 +149,45 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
|
||||
let infoFlags = TelegramChannelGroupFlags()
|
||||
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
|
||||
}
|
||||
|
||||
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChannel {
|
||||
guard let lhs = lhs else {
|
||||
return rhs
|
||||
}
|
||||
|
||||
if case .personal = rhs.accessHash {
|
||||
return rhs
|
||||
}
|
||||
|
||||
var channelFlags = lhs.flags
|
||||
if rhs.flags.contains(.isVerified) {
|
||||
channelFlags.insert(.isVerified)
|
||||
} else {
|
||||
let _ = channelFlags.remove(.isVerified)
|
||||
}
|
||||
var info = lhs.info
|
||||
switch info {
|
||||
case .broadcast:
|
||||
break
|
||||
case .group:
|
||||
let infoFlags = TelegramChannelGroupFlags()
|
||||
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
|
||||
}
|
||||
|
||||
let accessHash: TelegramPeerAccessHash?
|
||||
if let rhsAccessHashValue = lhs.accessHash, case .personal = rhsAccessHashValue {
|
||||
accessHash = rhsAccessHashValue
|
||||
} else {
|
||||
accessHash = lhs.accessHash ?? rhs.accessHash
|
||||
}
|
||||
|
||||
return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights)
|
||||
}
|
||||
|
||||
|
@ -234,7 +234,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
||||
if let threadId = updatedMessage.threadId {
|
||||
let messageThreadId = makeThreadIdMessageId(peerId: updatedMessage.id.peerId, threadId: threadId)
|
||||
if let authorId = updatedMessage.authorId {
|
||||
updateMessageThreadStats(transaction: transaction, threadMessageId: messageThreadId, difference: 1, addedMessagePeers: [authorId])
|
||||
updateMessageThreadStats(transaction: transaction, threadMessageId: messageThreadId, difference: 1, addedMessagePeers: [ReplyThreadUserMessage(id: authorId, messageId: updatedId, isOutgoing: true)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ private final class HistoryPreloadEntry: Comparable {
|
||||
|> mapToSignal { download -> Signal<Never, NoError> in
|
||||
switch hole.hole {
|
||||
case let .peer(peerHole):
|
||||
return fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .download(download), postbox: postbox, peerId: peerHole.peerId, namespace: peerHole.namespace, direction: hole.direction, space: .everywhere, threadId: nil, count: 60)
|
||||
return fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .download(download), postbox: postbox, peerInput: .direct(peerId: peerHole.peerId, threadId: nil), namespace: peerHole.namespace, direction: hole.direction, space: .everywhere, count: 60)
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,39 @@ func withResolvedAssociatedMessages<T>(postbox: Postbox, source: FetchMessageHis
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryHoleSource, postbox: Postbox, peerId: PeerId, namespace: MessageId.Namespace, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, threadId: MessageId?, count rawCount: Int) -> Signal<IndexSet, NoError> {
|
||||
enum FetchMessageHistoryHoleThreadInput: CustomStringConvertible {
|
||||
case direct(peerId: PeerId, threadId: Int64?)
|
||||
case threadFromChannel(channelMessageId: MessageId)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .direct(peerId, threadId):
|
||||
return "direct(\(peerId), \(String(describing: threadId))"
|
||||
case let .threadFromChannel(channelMessageId):
|
||||
return "threadFromChannel(peerId: \(channelMessageId.peerId), postId: \(channelMessageId.id)"
|
||||
}
|
||||
}
|
||||
|
||||
var requestThreadId: MessageId? {
|
||||
switch self {
|
||||
case let .direct(peerId, threadId):
|
||||
if let threadId = threadId {
|
||||
return makeThreadIdMessageId(peerId: peerId, threadId: threadId)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let .threadFromChannel(channelMessageId):
|
||||
return channelMessageId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FetchMessageHistoryHoleResult: Equatable {
|
||||
var removedIndices: IndexSet
|
||||
var strictRemovedIndices: IndexSet
|
||||
}
|
||||
|
||||
func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryHoleSource, postbox: Postbox, peerInput: FetchMessageHistoryHoleThreadInput, namespace: MessageId.Namespace, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, count rawCount: Int) -> Signal<FetchMessageHistoryHoleResult, NoError> {
|
||||
let count = min(100, rawCount)
|
||||
|
||||
return postbox.stateView()
|
||||
@ -137,389 +169,377 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
|> mapToSignal { _ -> Signal<IndexSet, NoError> in
|
||||
return postbox.transaction { transaction -> (Peer?, Int32) in
|
||||
var hash: Int32 = 0
|
||||
switch space {
|
||||
case .everywhere:
|
||||
if let threadId = threadId {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
}
|
||||
|
||||
//request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadId.id, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
|
||||
}
|
||||
default:
|
||||
break
|
||||
|> mapToSignal { _ -> Signal<FetchMessageHistoryHoleResult, NoError> in
|
||||
return postbox.transaction { transaction -> (Api.InputPeer?, Int32) in
|
||||
switch peerInput {
|
||||
case let .direct(peerId, _):
|
||||
return (transaction.getPeer(peerId).flatMap(forceApiInputPeer), 0)
|
||||
case let .threadFromChannel(channelMessageId):
|
||||
return (transaction.getPeer(channelMessageId.peerId).flatMap(forceApiInputPeer), 0)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { (inputPeer, hash) -> Signal<FetchMessageHistoryHoleResult, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(), strictRemovedIndices: IndexSet()))
|
||||
}
|
||||
|
||||
return (transaction.getPeer(peerId), hash)
|
||||
}
|
||||
|> mapToSignal { (peer, hash) -> Signal<IndexSet, NoError> in
|
||||
guard let peer = peer else {
|
||||
return .single(IndexSet())
|
||||
}
|
||||
if let inputPeer = forceApiInputPeer(peer) {
|
||||
print("fetchMessageHistoryHole for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
|
||||
Logger.shared.log("fetchMessageHistoryHole", "fetch for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
|
||||
let request: Signal<Api.messages.Messages, MTRpcError>
|
||||
var implicitelyFillHole = false
|
||||
let minMaxRange: ClosedRange<MessageId.Id>
|
||||
|
||||
switch space {
|
||||
case .everywhere:
|
||||
if let threadId = threadId {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
print("fetchMessageHistoryHole for \(peerInput) direction \(direction) space \(space)")
|
||||
Logger.shared.log("fetchMessageHistoryHole", "fetch for \(peerInput) direction \(direction) space \(space)")
|
||||
let request: Signal<Api.messages.Messages, MTRpcError>
|
||||
var implicitelyFillHole = false
|
||||
let minMaxRange: ClosedRange<MessageId.Id>
|
||||
|
||||
switch space {
|
||||
case .everywhere:
|
||||
if let requestThreadId = peerInput.requestThreadId {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
|
||||
minMaxRange = 1 ... (Int32.max - 1)
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadId.id, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
|
||||
} else {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
minMaxRange = 1 ... Int32.max - 1
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
|
||||
}
|
||||
case let .tag(tag):
|
||||
assert(tag.containsSingleElement)
|
||||
if tag == .unseenPersonalMessage {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
|
||||
minMaxRange = 1 ... Int32.max - 1
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
} else if tag == .liveLocation {
|
||||
let selectedLimit = count
|
||||
|
||||
switch direction {
|
||||
case .aroundId, .range:
|
||||
implicitelyFillHole = true
|
||||
}
|
||||
minMaxRange = 1 ... (Int32.max - 1)
|
||||
request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0))
|
||||
} else if let filter = messageFilterForTagMask(tag) {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
|
||||
minMaxRange = 1 ... (Int32.max - 1)
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
|
||||
} else {
|
||||
assertionFailure()
|
||||
minMaxRange = 1 ... 1
|
||||
request = .never()
|
||||
}
|
||||
}
|
||||
|
||||
return request
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<IndexSet, NoError> in
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
var channelPts: Int32?
|
||||
switch result {
|
||||
case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messagesSlice(_, _, _, messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
channelPts = pts
|
||||
case .messagesNotModified:
|
||||
messages = []
|
||||
chats = []
|
||||
users = []
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
var storeMessages: [StoreMessage] = []
|
||||
|
||||
for message in messages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message, namespace: namespace) {
|
||||
if let channelPts = channelPts {
|
||||
var attributes = storeMessage.attributes
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
|
||||
storeMessages.append(storeMessage.withUpdatedAttributes(attributes))
|
||||
} else {
|
||||
storeMessages.append(storeMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return withResolvedAssociatedMessages(postbox: postbox, source: source, peers: Dictionary(peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages -> IndexSet in
|
||||
let _ = transaction.addMessages(storeMessages, location: .Random)
|
||||
let _ = transaction.addMessages(additionalMessages, location: .Random)
|
||||
var filledRange: ClosedRange<MessageId.Id>
|
||||
let ids = storeMessages.compactMap { message -> MessageId.Id? in
|
||||
switch message.id {
|
||||
case let .Id(id):
|
||||
switch space {
|
||||
case let .tag(tag):
|
||||
if !message.tags.contains(tag) {
|
||||
return nil
|
||||
} else {
|
||||
return id.id
|
||||
}
|
||||
case .everywhere:
|
||||
return id.id
|
||||
}
|
||||
case .Partial:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if ids.count == 0 || implicitelyFillHole {
|
||||
filledRange = minMaxRange
|
||||
} else {
|
||||
let messageRange = ids.min()! ... ids.max()!
|
||||
switch direction {
|
||||
case let .aroundId(aroundId):
|
||||
filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound)
|
||||
if threadId != nil {
|
||||
if ids.count <= count / 2 - 1 {
|
||||
filledRange = minMaxRange
|
||||
}
|
||||
}
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
let minBound = start.id
|
||||
let maxBound = messageRange.upperBound
|
||||
filledRange = min(minBound, maxBound) ... max(minBound, maxBound)
|
||||
} else {
|
||||
let minBound = messageRange.lowerBound
|
||||
let maxBound = start.id
|
||||
filledRange = min(minBound, maxBound) ... max(minBound, maxBound)
|
||||
}
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
|
||||
minMaxRange = 1 ... (Int32.max - 1)
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: requestThreadId.id, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
|
||||
} else {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
minMaxRange = 1 ... Int32.max - 1
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
|
||||
}
|
||||
case let .tag(tag):
|
||||
assert(tag.containsSingleElement)
|
||||
if tag == .unseenPersonalMessage {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
|
||||
minMaxRange = 1 ... Int32.max - 1
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
} else if tag == .liveLocation {
|
||||
let selectedLimit = count
|
||||
|
||||
switch direction {
|
||||
case .aroundId, .range:
|
||||
implicitelyFillHole = true
|
||||
}
|
||||
minMaxRange = 1 ... (Int32.max - 1)
|
||||
request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0))
|
||||
} else if let filter = messageFilterForTagMask(tag) {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
|
||||
minMaxRange = 1 ... (Int32.max - 1)
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
|
||||
} else {
|
||||
assertionFailure()
|
||||
minMaxRange = 1 ... 1
|
||||
request = .never()
|
||||
}
|
||||
}
|
||||
|
||||
return request
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<FetchMessageHistoryHoleResult, NoError> in
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
var channelPts: Int32?
|
||||
switch result {
|
||||
case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messagesSlice(_, _, _, messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
channelPts = pts
|
||||
case .messagesNotModified:
|
||||
messages = []
|
||||
chats = []
|
||||
users = []
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
var storeMessages: [StoreMessage] = []
|
||||
|
||||
for message in messages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message, namespace: namespace) {
|
||||
if let channelPts = channelPts {
|
||||
var attributes = storeMessage.attributes
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
|
||||
storeMessages.append(storeMessage.withUpdatedAttributes(attributes))
|
||||
} else {
|
||||
storeMessages.append(storeMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return withResolvedAssociatedMessages(postbox: postbox, source: source, peers: Dictionary(peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages -> FetchMessageHistoryHoleResult in
|
||||
let _ = transaction.addMessages(storeMessages, location: .Random)
|
||||
let _ = transaction.addMessages(additionalMessages, location: .Random)
|
||||
var filledRange: ClosedRange<MessageId.Id>
|
||||
var strictFilledIndices: IndexSet
|
||||
let ids = storeMessages.compactMap { message -> MessageId.Id? in
|
||||
switch message.id {
|
||||
case let .Id(id):
|
||||
switch space {
|
||||
case let .tag(tag):
|
||||
if !message.tags.contains(tag) {
|
||||
return nil
|
||||
} else {
|
||||
return id.id
|
||||
}
|
||||
case .everywhere:
|
||||
return id.id
|
||||
}
|
||||
case .Partial:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if ids.count == 0 || implicitelyFillHole {
|
||||
filledRange = minMaxRange
|
||||
strictFilledIndices = IndexSet()
|
||||
} else {
|
||||
let messageRange = ids.min()! ... ids.max()!
|
||||
switch direction {
|
||||
case let .aroundId(aroundId):
|
||||
filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound)
|
||||
strictFilledIndices = IndexSet(integersIn: Int(min(aroundId.id, messageRange.lowerBound)) ... Int(max(aroundId.id, messageRange.upperBound)))
|
||||
if peerInput.requestThreadId != nil {
|
||||
if ids.count <= count / 2 - 1 {
|
||||
filledRange = minMaxRange
|
||||
}
|
||||
}
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
let minBound = start.id
|
||||
let maxBound = messageRange.upperBound
|
||||
filledRange = min(minBound, maxBound) ... max(minBound, maxBound)
|
||||
|
||||
var maxStrictIndex = max(minBound, maxBound)
|
||||
maxStrictIndex = min(maxStrictIndex, messageRange.upperBound)
|
||||
strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex))
|
||||
} else {
|
||||
let minBound = messageRange.lowerBound
|
||||
let maxBound = start.id
|
||||
filledRange = min(minBound, maxBound) ... max(minBound, maxBound)
|
||||
|
||||
var maxStrictIndex = max(minBound, maxBound)
|
||||
maxStrictIndex = min(maxStrictIndex, messageRange.upperBound)
|
||||
strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch peerInput {
|
||||
case let .direct(peerId, threadId):
|
||||
if let threadId = threadId {
|
||||
transaction.removeThreadIndexHole(peerId: peerId, threadId: makeMessageThreadId(threadId), namespace: namespace, space: .everywhere, range: filledRange)
|
||||
for range in strictFilledIndices.rangeView {
|
||||
transaction.removeThreadIndexHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: Int32(range.lowerBound) ... Int32(range.upperBound))
|
||||
}
|
||||
} else {
|
||||
transaction.removeHole(peerId: peerId, namespace: namespace, space: space, range: filledRange)
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers + additionalPeers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences)
|
||||
|
||||
print("fetchMessageHistoryHole for \(peer.id) \(peer.debugDisplayTitle) space \(space) threadId: \(String(describing: threadId)) done")
|
||||
|
||||
return IndexSet(integersIn: Int(filledRange.lowerBound) ... Int(filledRange.upperBound))
|
||||
case .threadFromChannel:
|
||||
break
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers + additionalPeers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences)
|
||||
|
||||
print("fetchMessageHistoryHole for \(peerInput) space \(space) done")
|
||||
|
||||
return FetchMessageHistoryHoleResult(
|
||||
removedIndices: IndexSet(integersIn: Int(filledRange.lowerBound) ... Int(filledRange.upperBound)),
|
||||
strictRemovedIndices: strictFilledIndices
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -666,10 +686,11 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId
|
||||
}
|
||||
}
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ func managedMessageHistoryHoles(accountPeerId: PeerId, network: Network, postbox
|
||||
for (entry, disposable) in added {
|
||||
switch entry.hole {
|
||||
case let .peer(hole):
|
||||
disposable.set(fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .network(network), postbox: postbox, peerId: hole.peerId, namespace: hole.namespace, direction: entry.direction, space: entry.space, threadId: nil, count: entry.count).start())
|
||||
disposable.set(fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .network(network), postbox: postbox, peerInput: .direct(peerId: hole.peerId, threadId: nil), namespace: hole.namespace, direction: entry.direction, space: entry.space, count: entry.count).start())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -43,35 +43,31 @@ private class ReplyThreadHistoryContextImpl {
|
||||
private var readStateDisposable: Disposable?
|
||||
private let readDisposable = MetaDisposable()
|
||||
|
||||
init(queue: Queue, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
|
||||
init(queue: Queue, account: Account, data: ChatReplyThreadMessage) {
|
||||
self.queue = queue
|
||||
self.account = account
|
||||
self.messageId = messageId
|
||||
self.messageId = data.messageId
|
||||
|
||||
self.maxReadOutgoingMessageIdValue = maxReadOutgoingMessageId
|
||||
self.maxReadOutgoingMessageIdValue = data.maxReadOutgoingMessageId
|
||||
self.maxReadOutgoingMessageId.set(.single(self.maxReadOutgoingMessageIdValue))
|
||||
|
||||
self.initialStateDisposable = (account.postbox.transaction { transaction -> State in
|
||||
var indices = transaction.getThreadIndexHoles(peerId: messageId.peerId, threadId: makeMessageThreadId(messageId), namespace: Namespaces.Message.Cloud)
|
||||
switch maxMessage {
|
||||
case .unknown:
|
||||
indices.insert(integersIn: 1 ..< Int(Int32.max - 1))
|
||||
case let .known(maxMessageId):
|
||||
indices.insert(integersIn: 1 ..< Int(Int32.max - 1))
|
||||
/*if let maxMessageId = maxMessageId {
|
||||
let topMessage = transaction.getMessagesWithThreadId(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud, threadId: makeMessageThreadId(messageId), from: MessageIndex.upperBound(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud), includeFrom: false, to: MessageIndex.lowerBound(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud), limit: 1).first
|
||||
if let topMessage = topMessage {
|
||||
if maxMessageId.id < maxMessageId.id {
|
||||
indices.insert(integersIn: Int(topMessage.id.id + 1) ..< Int(Int32.max - 1))
|
||||
}
|
||||
} else {
|
||||
indices.insert(integersIn: 1 ..< Int(Int32.max - 1))
|
||||
}
|
||||
} else {
|
||||
indices = IndexSet()
|
||||
}*/
|
||||
var indices = transaction.getThreadIndexHoles(peerId: data.messageId.peerId, threadId: makeMessageThreadId(data.messageId), namespace: Namespaces.Message.Cloud)
|
||||
indices.subtract(data.initialFilledHoles)
|
||||
|
||||
let isParticipant = transaction.getPeerChatListIndex(data.messageId.peerId) != nil
|
||||
if isParticipant {
|
||||
let historyHoles = transaction.getHoles(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud)
|
||||
indices.formIntersection(historyHoles)
|
||||
}
|
||||
return State(messageId: messageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
|
||||
if let maxMessageId = data.maxMessage {
|
||||
indices.remove(integersIn: Int(maxMessageId.id + 1) ..< Int(Int32.max))
|
||||
} else {
|
||||
indices.removeAll()
|
||||
}
|
||||
|
||||
return State(messageId: data.messageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadIncomingMessageId: data.maxReadIncomingMessageId, maxReadOutgoingMessageId: data.maxReadOutgoingMessageId)
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] state in
|
||||
guard let strongSelf = self else {
|
||||
@ -108,8 +104,8 @@ private class ReplyThreadHistoryContextImpl {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let value = outgoing[messageId] {
|
||||
strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud, id: value)
|
||||
if let value = outgoing[data.messageId] {
|
||||
strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud, id: value)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -129,7 +125,7 @@ private class ReplyThreadHistoryContextImpl {
|
||||
return
|
||||
}
|
||||
if var currentHoles = strongSelf.stateValue?.holeIndices[Namespaces.Message.Cloud] {
|
||||
currentHoles.subtract(removedHoleIndices)
|
||||
currentHoles.subtract(removedHoleIndices.removedIndices)
|
||||
strongSelf.stateValue?.holeIndices[Namespaces.Message.Cloud] = currentHoles
|
||||
}
|
||||
}))
|
||||
@ -139,11 +135,11 @@ private class ReplyThreadHistoryContextImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchHole(entry: MessageHistoryHolesViewEntry) -> Signal<IndexSet, NoError> {
|
||||
private func fetchHole(entry: MessageHistoryHolesViewEntry) -> Signal<FetchMessageHistoryHoleResult, NoError> {
|
||||
switch entry.hole {
|
||||
case let .peer(hole):
|
||||
let fetchCount = min(entry.count, 100)
|
||||
return fetchMessageHistoryHole(accountPeerId: self.account.peerId, source: .network(self.account.network), postbox: self.account.postbox, peerId: hole.peerId, namespace: hole.namespace, direction: entry.direction, space: entry.space, threadId: hole.threadId.flatMap { makeThreadIdMessageId(peerId: self.messageId.peerId, threadId: $0) }, count: fetchCount)
|
||||
return fetchMessageHistoryHole(accountPeerId: self.account.peerId, source: .network(self.account.network), postbox: self.account.postbox, peerInput: .direct(peerId: hole.peerId, threadId: hole.threadId), namespace: hole.namespace, direction: entry.direction, space: entry.space, count: fetchCount)
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,10 +253,10 @@ public class ReplyThreadHistoryContext {
|
||||
}
|
||||
}
|
||||
|
||||
public init(account: Account, peerId: PeerId, threadMessageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
|
||||
public init(account: Account, peerId: PeerId, data: ChatReplyThreadMessage) {
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return ReplyThreadHistoryContextImpl(queue: queue, account: account, messageId: threadMessageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
return ReplyThreadHistoryContextImpl(queue: queue, account: account, data: data)
|
||||
})
|
||||
}
|
||||
|
||||
@ -271,45 +267,59 @@ public class ReplyThreadHistoryContext {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatReplyThreadMessage {
|
||||
public enum MaxMessage: Equatable {
|
||||
case unknown
|
||||
case known(MessageId?)
|
||||
}
|
||||
|
||||
public struct ChatReplyThreadMessage: Equatable {
|
||||
public var messageId: MessageId
|
||||
public var maxMessage: MaxMessage
|
||||
public var isChannelPost: Bool
|
||||
public var maxMessage: MessageId?
|
||||
public var maxReadIncomingMessageId: MessageId?
|
||||
public var maxReadOutgoingMessageId: MessageId?
|
||||
public var initialFilledHoles: IndexSet
|
||||
|
||||
public init(messageId: MessageId, maxMessage: MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
|
||||
fileprivate init(messageId: MessageId, isChannelPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, initialFilledHoles: IndexSet) {
|
||||
self.messageId = messageId
|
||||
self.isChannelPost = isChannelPost
|
||||
self.maxMessage = maxMessage
|
||||
self.maxReadIncomingMessageId = maxReadIncomingMessageId
|
||||
self.maxReadOutgoingMessageId = maxReadOutgoingMessageId
|
||||
self.initialFilledHoles = initialFilledHoles
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageId) -> Signal<ChatReplyThreadMessage?, NoError> {
|
||||
public enum FetchChannelReplyThreadMessageError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageId) -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
|> mapToSignal { inputPeer -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .single(nil)
|
||||
return .fail(.generic)
|
||||
}
|
||||
let discussionMessage: Signal<Api.messages.DiscussionMessage?, NoError> = account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: messageId.id))
|
||||
|
||||
let replyInfo = Promise<AccountViewTracker.UpdatedMessageReplyInfo?>()
|
||||
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))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.DiscussionMessage?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
return discussionMessage
|
||||
|> mapToSignal { discussionMessage -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
|> mapToSignal { discussionMessage -> Signal<DiscussionMessage?, NoError> in
|
||||
guard let discussionMessage = discussionMessage else {
|
||||
return .single(nil)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> ChatReplyThreadMessage? in
|
||||
return account.postbox.transaction { transaction -> DiscussionMessage? in
|
||||
switch discussionMessage {
|
||||
case let .discussionMessage(_, messages, maxId, readInboxMaxId, readOutboxMaxId, chats, users):
|
||||
let parsedMessages = messages.compactMap { message -> StoreMessage? in
|
||||
@ -344,19 +354,20 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
let resolvedMaxMessage: ChatReplyThreadMessage.MaxMessage
|
||||
let resolvedMaxMessage: MessageId?
|
||||
if let maxId = maxId {
|
||||
resolvedMaxMessage = .known(MessageId(
|
||||
resolvedMaxMessage = MessageId(
|
||||
peerId: parsedIndex.id.peerId,
|
||||
namespace: Namespaces.Message.Cloud,
|
||||
id: maxId
|
||||
))
|
||||
)
|
||||
} else {
|
||||
resolvedMaxMessage = .known(nil)
|
||||
resolvedMaxMessage = nil
|
||||
}
|
||||
|
||||
return ChatReplyThreadMessage(
|
||||
return DiscussionMessage(
|
||||
messageId: parsedIndex.id,
|
||||
isChannelPost: true,
|
||||
maxMessage: resolvedMaxMessage,
|
||||
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
|
||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||
@ -368,5 +379,172 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
}
|
||||
}
|
||||
}
|
||||
let discussionMessageSignal = (replyInfo.get()
|
||||
|> mapToSignal { replyInfo -> Signal<DiscussionMessage?, NoError> in
|
||||
guard let replyInfo = replyInfo else {
|
||||
return .single(nil)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> DiscussionMessage? in
|
||||
let isParticipant = transaction.getPeerChatListIndex(replyInfo.commentsPeerId) != nil
|
||||
|
||||
guard isParticipant else {
|
||||
return nil
|
||||
}
|
||||
var foundDiscussionMessageId: MessageId?
|
||||
transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in
|
||||
for attribute in attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
if attribute.messageId == messageId {
|
||||
foundDiscussionMessageId = id
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if foundDiscussionMessageId != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
guard let discussionMessageId = foundDiscussionMessageId else {
|
||||
return nil
|
||||
}
|
||||
return DiscussionMessage(
|
||||
messageId: discussionMessageId,
|
||||
isChannelPost: true,
|
||||
maxMessage: replyInfo.maxMessageId,
|
||||
maxReadIncomingMessageId: replyInfo.maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: nil
|
||||
)
|
||||
}
|
||||
})
|
||||
|> mapToSignal { result -> Signal<DiscussionMessage?, NoError> in
|
||||
if let result = result {
|
||||
return .single(result)
|
||||
} else {
|
||||
return remoteDiscussionMessageSignal
|
||||
}
|
||||
}
|
||||
let discussionMessage = Promise<DiscussionMessage?>()
|
||||
discussionMessage.set(discussionMessageSignal)
|
||||
|
||||
let preloadedHistoryPosition: Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> = replyInfo.get()
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
|> mapToSignal { replyInfo -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in
|
||||
if let replyInfo = replyInfo {
|
||||
return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?) in
|
||||
var threadInput: FetchMessageHistoryHoleThreadInput = .threadFromChannel(channelMessageId: messageId)
|
||||
var threadMessageId: MessageId?
|
||||
transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in
|
||||
for attribute in attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
if attribute.messageId == messageId {
|
||||
threadMessageId = id
|
||||
threadInput = .direct(peerId: id.peerId, threadId: makeMessageThreadId(id))
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return (threadInput, replyInfo.commentsPeerId, threadMessageId, replyInfo.maxReadIncomingMessageId, replyInfo.maxMessageId)
|
||||
}
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
} else {
|
||||
return discussionMessage.get()
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
|> mapToSignal { discussionMessage -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in
|
||||
guard let discussionMessage = discussionMessage else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
let topMessageId = discussionMessage.messageId
|
||||
let commentsPeerId = topMessageId.peerId
|
||||
return .single((.direct(peerId: commentsPeerId, threadId: makeMessageThreadId(topMessageId)), commentsPeerId, discussionMessage.messageId, discussionMessage.maxReadIncomingMessageId, discussionMessage.maxMessage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let preloadedHistory = preloadedHistoryPosition
|
||||
|> mapToSignal { peerInput, commentsPeerId, threadMessageId, aroundMessageId, maxMessageId -> Signal<FetchMessageHistoryHoleResult, FetchChannelReplyThreadMessageError> in
|
||||
guard let maxMessageId = maxMessageId else {
|
||||
return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(integersIn: 1 ..< Int(Int32.max - 1)), strictRemovedIndices: IndexSet()))
|
||||
}
|
||||
return account.postbox.transaction { transaction -> Signal<FetchMessageHistoryHoleResult, FetchChannelReplyThreadMessageError> in
|
||||
if let threadMessageId = threadMessageId {
|
||||
var holes = transaction.getThreadIndexHoles(peerId: threadMessageId.peerId, threadId: makeMessageThreadId(threadMessageId), namespace: Namespaces.Message.Cloud)
|
||||
holes.remove(integersIn: Int(maxMessageId.id + 1) ..< Int(Int32.max))
|
||||
|
||||
let isParticipant = transaction.getPeerChatListIndex(commentsPeerId) != nil
|
||||
if isParticipant {
|
||||
let historyHoles = transaction.getHoles(peerId: commentsPeerId, namespace: Namespaces.Message.Cloud)
|
||||
holes.formIntersection(historyHoles)
|
||||
}
|
||||
|
||||
let anchor: HistoryViewInputAnchor
|
||||
if let aroundMessageId = aroundMessageId {
|
||||
anchor = .message(aroundMessageId)
|
||||
} else {
|
||||
anchor = .upperBound
|
||||
}
|
||||
|
||||
let testView = transaction.getMessagesHistoryViewState(
|
||||
input: .external(MessageHistoryViewExternalInput(
|
||||
peerId: commentsPeerId,
|
||||
threadId: makeMessageThreadId(threadMessageId),
|
||||
maxReadIncomingMessageId: nil,
|
||||
maxReadOutgoingMessageId: nil,
|
||||
holes: [
|
||||
Namespaces.Message.Cloud: holes
|
||||
]
|
||||
)),
|
||||
count: 30,
|
||||
clipHoles: true,
|
||||
anchor: anchor,
|
||||
namespaces: .not(Namespaces.Message.allScheduled)
|
||||
)
|
||||
if !testView.isLoading {
|
||||
return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(), strictRemovedIndices: IndexSet()))
|
||||
}
|
||||
}
|
||||
|
||||
let direction: MessageHistoryViewRelativeHoleDirection
|
||||
if let aroundMessageId = aroundMessageId {
|
||||
direction = .aroundId(aroundMessageId)
|
||||
} else {
|
||||
direction = .range(start: MessageId(peerId: commentsPeerId, namespace: Namespaces.Message.Cloud, id: Int32.max - 1), end: MessageId(peerId: commentsPeerId, namespace: Namespaces.Message.Cloud, id: 1))
|
||||
}
|
||||
return fetchMessageHistoryHole(accountPeerId: account.peerId, source: .network(account.network), postbox: account.postbox, peerInput: peerInput, namespace: Namespaces.Message.Cloud, direction: direction, space: .everywhere, count: 30)
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
}
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
discussionMessage.get()
|
||||
|> castError(FetchChannelReplyThreadMessageError.self),
|
||||
preloadedHistory
|
||||
)
|
||||
|> mapToSignal { discussionMessage, initialFilledHoles -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
|
||||
guard let discussionMessage = discussionMessage else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
|
||||
for range in initialFilledHoles.strictRemovedIndices.rangeView {
|
||||
transaction.removeThreadIndexHole(peerId: discussionMessage.messageId.peerId, threadId: makeMessageThreadId(discussionMessage.messageId), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound))
|
||||
}
|
||||
|
||||
return .single(ChatReplyThreadMessage(
|
||||
messageId: discussionMessage.messageId,
|
||||
isChannelPost: discussionMessage.isChannelPost,
|
||||
maxMessage: discussionMessage.maxMessage,
|
||||
maxReadIncomingMessageId: discussionMessage.maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: discussionMessage.maxReadOutgoingMessageId,
|
||||
initialFilledHoles: initialFilledHoles.removedIndices
|
||||
))
|
||||
}
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -626,19 +626,26 @@ public func updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerRefe
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Peer, UpdatedRemotePeerError> in
|
||||
if let updatedPeer = result.first.flatMap(TelegramUser.init(user:)), updatedPeer.id == peer.id {
|
||||
return postbox.transaction { transaction -> Peer in
|
||||
updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
return updatedPeer
|
||||
}
|
||||
|> mapError { _ -> UpdatedRemotePeerError in
|
||||
return .generic
|
||||
}
|
||||
} else {
|
||||
guard let apiUser = result.first else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return postbox.transaction { transaction -> Peer? in
|
||||
guard let peer = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) else {
|
||||
return nil
|
||||
}
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
return peer
|
||||
}
|
||||
|> castError(UpdatedRemotePeerError.self)
|
||||
|> mapToSignal { peer -> Signal<Peer, UpdatedRemotePeerError> in
|
||||
if let peer = peer {
|
||||
return .single(peer)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if case let .group(id) = peer {
|
||||
return network.request(Api.functions.messages.getChats(id: [id]))
|
||||
|
@ -8,7 +8,7 @@ func parsedTelegramProfilePhoto(_ photo: Api.UserProfilePhoto) -> [TelegramMedia
|
||||
var representations: [TelegramMediaImageRepresentation] = []
|
||||
switch photo {
|
||||
case let .userProfilePhoto(flags, _, photoSmall, photoBig, dcId):
|
||||
let hasVideo = (flags & (1 << 0)) != 0
|
||||
let _ = (flags & (1 << 0)) != 0
|
||||
|
||||
let smallResource: TelegramMediaResource
|
||||
let fullSizeResource: TelegramMediaResource
|
||||
@ -138,4 +138,37 @@ extension TelegramUser {
|
||||
return TelegramUser(user: rhs)
|
||||
}
|
||||
}
|
||||
|
||||
static func merge(lhs: TelegramUser?, rhs: TelegramUser) -> TelegramUser {
|
||||
guard let lhs = lhs else {
|
||||
return rhs
|
||||
}
|
||||
if case .personal = rhs.accessHash {
|
||||
return rhs
|
||||
} else {
|
||||
var userFlags: UserInfoFlags = []
|
||||
if rhs.flags.contains(.isVerified) {
|
||||
userFlags.insert(.isVerified)
|
||||
}
|
||||
if rhs.flags.contains(.isSupport) {
|
||||
userFlags.insert(.isSupport)
|
||||
}
|
||||
if rhs.flags.contains(.isScam) {
|
||||
userFlags.insert(.isScam)
|
||||
}
|
||||
|
||||
let botInfo: BotUserInfo? = rhs.botInfo
|
||||
|
||||
let restrictionInfo: PeerAccessRestrictionInfo? = rhs.restrictionInfo
|
||||
|
||||
let accessHash: TelegramPeerAccessHash?
|
||||
if let rhsAccessHashValue = lhs.accessHash, case .personal = rhsAccessHashValue {
|
||||
accessHash = rhsAccessHashValue
|
||||
} else {
|
||||
accessHash = lhs.accessHash ?? rhs.accessHash
|
||||
}
|
||||
|
||||
return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: rhs.username, phone: lhs.phone, photo: rhs.photo.isEmpty ? lhs.photo : rhs.photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,9 @@ public func updateAccountPeerName(account: Account, firstName: String, lastName:
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
if let result = result {
|
||||
let peer = TelegramUser(user: result)
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { $1 })
|
||||
if let peer = TelegramUser.merge(transaction.getPeer(result.peerId) as? TelegramUser, rhs: result) {
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { $1 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,6 +44,7 @@ public func updateAbout(account: Account, about: String?) -> Signal<Void, Update
|
||||
return current
|
||||
}
|
||||
})
|
||||
} |> mapError { _ -> UpdateAboutError in return .generic }
|
||||
}
|
||||
|> castError(UpdateAboutError.self)
|
||||
}
|
||||
}
|
||||
|
@ -173,13 +173,14 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
return postbox.transaction { transaction -> Bool in
|
||||
switch result {
|
||||
case let .userFull(userFull):
|
||||
let telegramUser = TelegramUser(user: userFull.user)
|
||||
updatePeers(transaction: transaction, peers: [telegramUser], update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
if let telegramUser = TelegramUser.merge(transaction.getPeer(userFull.user.peerId) as? TelegramUser, rhs: userFull.user) {
|
||||
updatePeers(transaction: transaction, peers: [telegramUser], update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: userFull.notifySettings)])
|
||||
if let presence = TelegramUserPresence(apiUser: userFull.user) {
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: [telegramUser.id: presence])
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: [userFull.user.peerId: presence])
|
||||
}
|
||||
}
|
||||
transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, current in
|
||||
@ -267,10 +268,11 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
}
|
||||
}
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,10 +418,11 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
}
|
||||
}
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,10 +430,11 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
switch participantResult {
|
||||
case let .channelParticipant(_, users):
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,11 +29,17 @@ func updateMessageMedia(transaction: Transaction, id: MediaId, media: Media?) {
|
||||
}
|
||||
}
|
||||
|
||||
func updateMessageThreadStats(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [PeerId]) {
|
||||
struct ReplyThreadUserMessage {
|
||||
var id: PeerId
|
||||
var messageId: MessageId
|
||||
var isOutgoing: Bool
|
||||
}
|
||||
|
||||
func updateMessageThreadStats(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [ReplyThreadUserMessage]) {
|
||||
updateMessageThreadStatsInternal(transaction: transaction, threadMessageId: threadMessageId, difference: difference, addedMessagePeers: addedMessagePeers, allowChannel: false)
|
||||
}
|
||||
|
||||
private func updateMessageThreadStatsInternal(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [PeerId], allowChannel: Bool) {
|
||||
private func updateMessageThreadStatsInternal(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [ReplyThreadUserMessage], allowChannel: Bool) {
|
||||
guard let channel = transaction.getPeer(threadMessageId.peerId) as? TelegramChannel else {
|
||||
return
|
||||
}
|
||||
@ -77,7 +83,24 @@ private func updateMessageThreadStatsInternal(transaction: Transaction, threadMe
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ReplyThreadMessageAttribute {
|
||||
let count = max(0, attribute.count + countDifference)
|
||||
attributes[j] = ReplyThreadMessageAttribute(count: count, latestUsers: mergeLatestUsers(current: attribute.latestUsers, added: addedMessagePeers, isGroup: isGroup, isEmpty: count == 0), commentsPeerId: attribute.commentsPeerId, maxMessageId: attribute.maxMessageId, maxReadMessageId: attribute.maxReadMessageId)
|
||||
var maxMessageId = attribute.maxMessageId
|
||||
var maxReadMessageId = attribute.maxReadMessageId
|
||||
if let maxAddedId = addedMessagePeers.map(\.messageId.id).max() {
|
||||
if let currentMaxMessageId = maxMessageId {
|
||||
maxMessageId = max(currentMaxMessageId, maxAddedId)
|
||||
} else {
|
||||
maxMessageId = maxAddedId
|
||||
}
|
||||
}
|
||||
if let maxAddedReadId = addedMessagePeers.filter(\.isOutgoing).map(\.messageId.id).max() {
|
||||
if let currentMaxMessageId = maxReadMessageId {
|
||||
maxReadMessageId = max(currentMaxMessageId, maxAddedReadId)
|
||||
} else {
|
||||
maxReadMessageId = maxAddedReadId
|
||||
}
|
||||
}
|
||||
|
||||
attributes[j] = ReplyThreadMessageAttribute(count: count, latestUsers: mergeLatestUsers(current: attribute.latestUsers, added: addedMessagePeers.map(\.id), isGroup: isGroup, isEmpty: count == 0), commentsPeerId: attribute.commentsPeerId, maxMessageId: maxMessageId, maxReadMessageId: maxReadMessageId)
|
||||
} else if let attribute = attributes[j] as? SourceReferenceMessageAttribute {
|
||||
channelThreadMessageId = attribute.messageId
|
||||
}
|
||||
|
@ -44,6 +44,14 @@ public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?,
|
||||
transaction.updatePeersInternal(peers, update: { previous, updated in
|
||||
let peerId = updated.id
|
||||
|
||||
var updated = updated
|
||||
|
||||
if let previous = previous as? TelegramUser, let updatedUser = updated as? TelegramUser {
|
||||
updated = TelegramUser.merge(lhs: previous, rhs: updatedUser)
|
||||
} else if let previous = previous as? TelegramChannel, let updatedChannel = updated as? TelegramChannel {
|
||||
updated = mergeChannel(lhs: previous, rhs: updatedChannel)
|
||||
}
|
||||
|
||||
switch peerId.namespace {
|
||||
case Namespaces.Peer.CloudUser:
|
||||
break
|
||||
|
@ -303,9 +303,9 @@ public final class AccountContextImpl: AccountContext {
|
||||
switch location {
|
||||
case let .peer(peerId):
|
||||
return .peer(peerId)
|
||||
case let .replyThread(messageId, _, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
return .external(messageId.peerId, context.state)
|
||||
case let .replyThread(data):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
|
||||
return .external(data.messageId.peerId, context.state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,8 +313,8 @@ public final class AccountContextImpl: AccountContext {
|
||||
switch location {
|
||||
case .peer:
|
||||
return .single(nil)
|
||||
case let .replyThread(messageId, _, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
case let .replyThread(data):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
|
||||
return context.maxReadOutgoingMessageId
|
||||
}
|
||||
}
|
||||
@ -323,19 +323,19 @@ public final class AccountContextImpl: AccountContext {
|
||||
switch location {
|
||||
case .peer:
|
||||
let _ = applyMaxReadIndexInteractively(postbox: self.account.postbox, stateManager: self.account.stateManager, index: messageIndex).start()
|
||||
case let .replyThread(messageId, _, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
case let .replyThread(data):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
|
||||
context.applyMaxReadIndex(messageIndex: messageIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) -> ReplyThreadHistoryContext {
|
||||
private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, account: Account, data: ChatReplyThreadMessage) -> ReplyThreadHistoryContext {
|
||||
let holder = holder.modify { current in
|
||||
if let current = current as? ChatLocationContextHolderImpl {
|
||||
return current
|
||||
} else {
|
||||
return ChatLocationContextHolderImpl(account: account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
return ChatLocationContextHolderImpl(account: account, data: data)
|
||||
}
|
||||
} as! ChatLocationContextHolderImpl
|
||||
return holder.context
|
||||
@ -344,8 +344,8 @@ private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, acc
|
||||
private final class ChatLocationContextHolderImpl: ChatLocationContextHolder {
|
||||
let context: ReplyThreadHistoryContext
|
||||
|
||||
init(account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
|
||||
self.context = ReplyThreadHistoryContext(account: account, peerId: messageId.peerId, threadMessageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
init(account: Account, data: ChatReplyThreadMessage) {
|
||||
self.context = ReplyThreadHistoryContext(account: account, peerId: data.messageId.peerId, data: data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,8 @@ extension ChatLocation {
|
||||
switch self {
|
||||
case let .peer(peerId):
|
||||
return peerId
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
return messageId.peerId
|
||||
case let .replyThread(replyThreadMessage):
|
||||
return replyThreadMessage.messageId.peerId
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -371,16 +371,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case let .peer(peerId):
|
||||
locationBroadcastPanelSource = .peer(peerId)
|
||||
self.chatLocationInfoData = .peer(Promise())
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
case let .replyThread(replyThreadMessage):
|
||||
locationBroadcastPanelSource = .none
|
||||
let promise = Promise<Message?>()
|
||||
let key = PostboxViewKey.messages([messageId])
|
||||
let key = PostboxViewKey.messages([replyThreadMessage.messageId])
|
||||
promise.set(context.account.postbox.combinedView(keys: [key])
|
||||
|> map { views -> Message? in
|
||||
guard let view = views.views[key] as? MessagesView else {
|
||||
return nil
|
||||
}
|
||||
return view.messages[messageId]
|
||||
return view.messages[replyThreadMessage.messageId]
|
||||
})
|
||||
self.chatLocationInfoData = .replyThread(promise)
|
||||
}
|
||||
@ -481,8 +481,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
var openChatLocation = strongSelf.chatLocation
|
||||
if case let .replyThread(messageId, _, _, _, _) = openChatLocation {
|
||||
if message.threadId != makeMessageThreadId(messageId) {
|
||||
if case let .replyThread(replyThreadMessage) = openChatLocation {
|
||||
if message.threadId != makeMessageThreadId(replyThreadMessage.messageId) {
|
||||
openChatLocation = .peer(message.id.peerId)
|
||||
}
|
||||
}
|
||||
@ -1601,7 +1601,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
if let strongSelf = self {
|
||||
if case .replyThread(message.id, _, _, _, _) = strongSelf.chatLocation {
|
||||
if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation, replyThreadMessage.messageId == message.id {
|
||||
return .none
|
||||
}
|
||||
|
||||
@ -1640,8 +1640,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch strongSelf.chatLocation {
|
||||
case let .peer(peerId):
|
||||
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
let peerId = messageId.peerId
|
||||
case let .replyThread(replyThreadMessage):
|
||||
let peerId = replyThreadMessage.messageId.peerId
|
||||
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
|
||||
}
|
||||
}, requestRedeliveryOfFailedMessages: { [weak self] id in
|
||||
@ -2191,11 +2191,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
ChatControllerImpl.openMessageReplies(context: strongSelf.context, navigationController: navigationController, present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, messageId: messageId, isChannelPost: isChannelPost, atMessage: nil)
|
||||
}
|
||||
strongSelf.openMessageReplies(messageId: messageId, isChannelPost: isChannelPost, atMessage: nil)
|
||||
}, openReplyThreadOriginalMessage: { [weak self] message in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -2211,9 +2207,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
if let threadMessageId = threadMessageId {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
ChatControllerImpl.openMessageReplies(context: strongSelf.context, navigationController: navigationController, present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, messageId: threadMessageId, isChannelPost: true, atMessage: attribute.messageId)
|
||||
strongSelf.openMessageReplies(messageId: threadMessageId, isChannelPost: true, atMessage: attribute.messageId)
|
||||
}
|
||||
} else {
|
||||
strongSelf.navigateToMessage(from: nil, to: .id(attribute.messageId))
|
||||
@ -2642,18 +2636,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}))
|
||||
} else if case let .replyThread(messagePromise) = self.chatLocationInfoData {
|
||||
let onlineMemberCount: Signal<Int32?, NoError> = .single(nil)
|
||||
let hasScheduledMessages: Signal<Bool, NoError> = .single(false)
|
||||
self.reportIrrelvantGeoNoticePromise.set(.single(nil))
|
||||
|
||||
let isReplyThread: Bool
|
||||
let replyThreadType: ChatTitleContent.ReplyThreadType
|
||||
switch chatLocation {
|
||||
case .peer:
|
||||
replyThreadType = .replies
|
||||
case let .replyThread(_, isChannelPost, _, _, _):
|
||||
isReplyThread = true
|
||||
if isChannelPost {
|
||||
case let .replyThread(replyThreadMessage):
|
||||
if replyThreadMessage.isChannelPost {
|
||||
replyThreadType = .comments
|
||||
} else {
|
||||
replyThreadType = .replies
|
||||
@ -2997,8 +2988,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch self.chatLocation {
|
||||
case let .peer(peerId):
|
||||
activitySpace = PeerActivitySpace(peerId: peerId, threadId: nil)
|
||||
case let .replyThread(threadMessageId, _, _, _, _):
|
||||
activitySpace = PeerActivitySpace(peerId: threadMessageId.peerId, threadId: makeMessageThreadId(threadMessageId))
|
||||
case let .replyThread(replyThreadMessage):
|
||||
activitySpace = PeerActivitySpace(peerId: replyThreadMessage.messageId.peerId, threadId: makeMessageThreadId(replyThreadMessage.messageId))
|
||||
}
|
||||
|
||||
self.inputActivityDisposable = (self.typingActivityPromise.get()
|
||||
@ -3416,11 +3407,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else if let _ = cachedData as? CachedSecretChatData {
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, _, _, _, _) = strongSelf.chatLocation {
|
||||
if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation {
|
||||
if isTopReplyThreadMessageShown {
|
||||
pinnedMessageId = nil
|
||||
} else {
|
||||
pinnedMessageId = messageId
|
||||
pinnedMessageId = replyThreadMessage.messageId
|
||||
}
|
||||
}
|
||||
|
||||
@ -5105,7 +5096,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if let navigationController = strongSelf.effectiveNavigationController {
|
||||
let subject: ChatControllerSubject? = sourceMessageId.flatMap(ChatControllerSubject.message)
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadResult.messageId, isChannelPost: false, maxMessage: replyThreadResult.maxMessage, maxReadIncomingMessageId: replyThreadResult.maxReadIncomingMessageId, maxReadOutgoingMessageId: replyThreadResult.maxReadOutgoingMessageId), subject: subject, keepStack: .always))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadResult), subject: subject, keepStack: .always))
|
||||
}
|
||||
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get()))
|
||||
|
||||
@ -5168,8 +5159,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let postbox = self.context.account.postbox
|
||||
let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:])
|
||||
var activityThreadId: Int64?
|
||||
if case let .replyThread(messageId, _, _, _, _) = self.chatLocation {
|
||||
activityThreadId = makeMessageThreadId(messageId)
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation {
|
||||
activityThreadId = makeMessageThreadId(replyThreadMessage.messageId)
|
||||
}
|
||||
self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: PeerActivitySpace(peerId: peerId, threadId: activityThreadId))
|
||||
|> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in
|
||||
@ -7566,8 +7557,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch self.chatLocation {
|
||||
case .peer:
|
||||
break
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
defaultReplyMessageId = messageId
|
||||
case let .replyThread(replyThreadMessage):
|
||||
defaultReplyMessageId = replyThreadMessage.messageId
|
||||
}
|
||||
|
||||
return messages.map { message in
|
||||
@ -7609,13 +7600,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
private func sendMessages(_ messages: [EnqueueMessage], commit: Bool = false) {
|
||||
let peerId: PeerId
|
||||
switch self.chatLocation {
|
||||
case let .peer(peerIdValue):
|
||||
peerId = peerIdValue
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
peerId = messageId.peerId
|
||||
}
|
||||
let peerId: PeerId = self.chatLocation.peerId
|
||||
|
||||
if commit || !self.presentationInterfaceState.isScheduledMessages {
|
||||
self.commitPurposefulAction()
|
||||
@ -8035,8 +8020,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch self.chatLocation {
|
||||
case .peer:
|
||||
break
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
searchTopMsgId = messageId
|
||||
case let .replyThread(replyThreadMessage):
|
||||
searchTopMsgId = replyThreadMessage.messageId
|
||||
}
|
||||
switch search.domain {
|
||||
case .everything:
|
||||
@ -8251,41 +8236,101 @@ 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?) {
|
||||
let foundIndex = Promise<ReplyThreadInfo?>()
|
||||
foundIndex.set(fetchAndPreloadReplyThreadInfo(context: context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId)))
|
||||
private func openMessageReplies(messageId: MessageId, isChannelPost: Bool, atMessage atMessageId: MessageId?) {
|
||||
guard let navigationController = self.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let progressSignal: Signal<Never, NoError> = Signal { [weak self] _ in
|
||||
guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else {
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let statusController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
present(statusController, nil)
|
||||
let previousId = controllerInteraction.currentMessageWithLoadingReplyThread
|
||||
controllerInteraction.currentMessageWithLoadingReplyThread = messageId
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(messageId)
|
||||
if let previousId = previousId {
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(previousId)
|
||||
}
|
||||
|
||||
return ActionDisposable {
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else {
|
||||
return
|
||||
}
|
||||
if controllerInteraction.currentMessageWithLoadingReplyThread == messageId {
|
||||
controllerInteraction.currentMessageWithLoadingReplyThread = nil
|
||||
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(messageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> runOn(.mainQueue())
|
||||
|
||||
let disposable = (foundIndex.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak statusController] result in
|
||||
statusController?.dismiss()
|
||||
let progress = (progressSignal
|
||||
|> delay(0.15, queue: .mainQueue())).start()
|
||||
|
||||
self.navigationActionDisposable.set((ChatControllerImpl.openMessageReplies(context: self.context, navigationController: navigationController, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, messageId: messageId, isChannelPost: isChannelPost, atMessage: atMessageId, displayModalProgress: false)
|
||||
|> afterDisposed {
|
||||
progress.dispose()
|
||||
}).start())
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
})
|
||||
|
||||
if let result = result {
|
||||
let chatLocation: ChatLocation = .replyThread(threadMessageId: result.message.messageId, isChannelPost: result.isChannelPost, maxMessage: result.message.maxMessage, maxReadIncomingMessageId: result.message.maxReadIncomingMessageId, maxReadOutgoingMessageId: result.message.maxReadOutgoingMessageId)
|
||||
|
||||
let subject: ChatControllerSubject?
|
||||
if let atMessageId = atMessageId {
|
||||
subject = .message(atMessageId)
|
||||
} else {
|
||||
subject = nil
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let statusController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
|
||||
if displayModalProgress {
|
||||
present(statusController, nil)
|
||||
}
|
||||
|
||||
let disposable = (foundIndex.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak statusController] result in
|
||||
if displayModalProgress {
|
||||
statusController?.dismiss()
|
||||
}
|
||||
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, subject: subject, activateInput: result.isEmpty, keepStack: .always))
|
||||
if let result = result {
|
||||
let chatLocation: ChatLocation = .replyThread(result.message)
|
||||
|
||||
let subject: ChatControllerSubject?
|
||||
if let atMessageId = atMessageId {
|
||||
subject = .message(atMessageId)
|
||||
} else {
|
||||
subject = nil
|
||||
}
|
||||
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, subject: subject, activateInput: result.isEmpty, keepStack: .always))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
})
|
||||
|
||||
cancelImpl = { [weak statusController] in
|
||||
disposable.dispose()
|
||||
statusController?.dismiss()
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
|
||||
return ActionDisposable {
|
||||
cancelImpl?()
|
||||
}
|
||||
})
|
||||
|
||||
cancelImpl = { [weak statusController] in
|
||||
disposable.dispose()
|
||||
statusController?.dismiss()
|
||||
}
|
||||
|> runOn(.mainQueue())
|
||||
}
|
||||
|
||||
public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, dropStack: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) {
|
||||
|
@ -130,6 +130,7 @@ public final class ChatControllerInteraction {
|
||||
var stickerSettings: ChatInterfaceStickerSettings
|
||||
var searchTextHighightState: (String, [MessageIndex])?
|
||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
||||
var currentMessageWithLoadingReplyThread: MessageId?
|
||||
|
||||
init(
|
||||
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,
|
||||
|
@ -136,10 +136,10 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
}
|
||||
|
||||
var addedThreadHead = false
|
||||
if case let .replyThread(messageId, isChannelPost, _, _, _) = location, view.earlierId == nil, !view.holeEarlier, !view.isLoading {
|
||||
if case let .replyThread(replyThreadMessage) = location, view.earlierId == nil, !view.holeEarlier, !view.isLoading {
|
||||
loop: for entry in view.additionalData {
|
||||
switch entry {
|
||||
case let .message(id, messages) where id == messageId:
|
||||
case let .message(id, messages) where id == replyThreadMessage.messageId:
|
||||
if !messages.isEmpty {
|
||||
let selection: ChatHistoryMessageSelection = .none
|
||||
|
||||
@ -172,7 +172,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
|
||||
let replyCount = view.entries.isEmpty ? 0 : 1
|
||||
|
||||
entries.insert(.ReplyCountEntry(messages[0].index, isChannelPost, replyCount, presentationData), at: 1)
|
||||
entries.insert(.ReplyCountEntry(messages[0].index, replyThreadMessage.isChannelPost, replyCount, presentationData), at: 1)
|
||||
}
|
||||
break loop
|
||||
default:
|
||||
|
@ -627,15 +627,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if !isAuxiliaryChat {
|
||||
additionalData.append(.totalUnreadState)
|
||||
}
|
||||
if case let .replyThread(messageId, _, _, _, _) = chatLocation {
|
||||
additionalData.append(.cachedPeerData(messageId.peerId))
|
||||
additionalData.append(.peerNotificationSettings(messageId.peerId))
|
||||
if messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
additionalData.append(.cacheEntry(cachedChannelAdminRanksEntryId(peerId: messageId.peerId)))
|
||||
additionalData.append(.peer(messageId.peerId))
|
||||
if case let .replyThread(replyThreadMessage) = chatLocation {
|
||||
additionalData.append(.cachedPeerData(replyThreadMessage.messageId.peerId))
|
||||
additionalData.append(.peerNotificationSettings(replyThreadMessage.messageId.peerId))
|
||||
if replyThreadMessage.messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
additionalData.append(.cacheEntry(cachedChannelAdminRanksEntryId(peerId: replyThreadMessage.messageId.peerId)))
|
||||
additionalData.append(.peer(replyThreadMessage.messageId.peerId))
|
||||
}
|
||||
|
||||
additionalData.append(.message(messageId))
|
||||
additionalData.append(.message(replyThreadMessage.messageId))
|
||||
}
|
||||
|
||||
let currentViewVersion = Atomic<Int?>(value: nil)
|
||||
@ -1173,7 +1173,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||
}
|
||||
if case .replyThread(message.id, _, _, _, _) = self.chatLocation {
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
case let .MessageGroupEntry(_, messages, _):
|
||||
@ -1203,7 +1203,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||
}
|
||||
if case .replyThread(message.id, _, _, _, _) = self.chatLocation {
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
}
|
||||
|
@ -302,8 +302,8 @@ enum ReplyThreadSubject {
|
||||
case groupMessage(MessageId)
|
||||
}
|
||||
|
||||
func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject) -> Signal<ReplyThreadInfo?, NoError> {
|
||||
let message: Signal<ChatReplyThreadMessage?, NoError>
|
||||
func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject) -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> {
|
||||
let message: Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError>
|
||||
switch subject {
|
||||
case let .channelPost(messageId):
|
||||
message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId)
|
||||
@ -312,19 +312,7 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
|
||||
}
|
||||
|
||||
return message
|
||||
|> mapToSignal { message -> Signal<ReplyThreadInfo?, NoError> in
|
||||
guard let message = message else {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
let isChannelPost: Bool
|
||||
switch subject {
|
||||
case .channelPost:
|
||||
isChannelPost = true
|
||||
case .groupMessage:
|
||||
isChannelPost = false
|
||||
}
|
||||
|
||||
|> mapToSignal { replyThreadMessage -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> in
|
||||
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
|
||||
let preloadSignal = preloadedChatHistoryViewForLocation(
|
||||
@ -333,13 +321,7 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
|
||||
id: 0
|
||||
),
|
||||
context: context,
|
||||
chatLocation: .replyThread(
|
||||
threadMessageId: message.messageId,
|
||||
isChannelPost: isChannelPost,
|
||||
maxMessage: message.maxMessage,
|
||||
maxReadIncomingMessageId: message.maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: message.maxReadOutgoingMessageId
|
||||
),
|
||||
chatLocation: .replyThread(replyThreadMessage),
|
||||
chatLocationContextHolder: chatLocationContextHolder,
|
||||
fixedCombinedReadStates: nil,
|
||||
tagMask: nil,
|
||||
@ -362,13 +344,14 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
|> map { isEmpty -> ReplyThreadInfo? in
|
||||
|> map { isEmpty -> ReplyThreadInfo in
|
||||
return ReplyThreadInfo(
|
||||
message: message,
|
||||
isChannelPost: isChannelPost,
|
||||
message: replyThreadMessage,
|
||||
isChannelPost: replyThreadMessage.isChannelPost,
|
||||
isEmpty: isEmpty,
|
||||
contextHolder: chatLocationContextHolder
|
||||
)
|
||||
}
|
||||
|> castError(FetchChannelReplyThreadMessageError.self)
|
||||
}
|
||||
}
|
||||
|
@ -468,8 +468,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
}
|
||||
|
||||
var isReplyThreadHead = false
|
||||
if case let .replyThread(messageId, _, _, _, _) = chatPresentationInterfaceState.chatLocation {
|
||||
isReplyThreadHead = messages[0].id == messageId
|
||||
if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation {
|
||||
isReplyThreadHead = messages[0].id == replyThreadMessage.messageId
|
||||
}
|
||||
|
||||
if !isReplyThreadHead, data.canReply {
|
||||
@ -614,8 +614,12 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replies"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { c, _ in
|
||||
let foundIndex = Promise<ChatReplyThreadMessage?>()
|
||||
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||
foundIndex.set(fetchChannelReplyThreadMessage(account: context.account, messageId: messages[0].id))
|
||||
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel {
|
||||
foundIndex.set(fetchChannelReplyThreadMessage(account: context.account, messageId: messages[0].id)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
return .single(nil)
|
||||
})
|
||||
}
|
||||
c.dismiss(completion: {
|
||||
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel {
|
||||
@ -745,8 +749,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
var threadMessageId: MessageId?
|
||||
if case let .replyThread(replyThread, _, _, _, _) = chatPresentationInterfaceState.chatLocation {
|
||||
threadMessageId = replyThread
|
||||
if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation {
|
||||
threadMessageId = replyThreadMessage.messageId
|
||||
}
|
||||
let _ = (exportMessageLink(account: context.account, peerId: message.id.peerId, messageId: message.id, isThread: threadMessageId != nil)
|
||||
|> map { result -> String? in
|
||||
|
@ -561,15 +561,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
} else if incoming {
|
||||
hasAvatar = true
|
||||
}
|
||||
case let .replyThread(messageId, isChannelPost, _, _, _):
|
||||
if messageId.peerId != item.context.account.peerId {
|
||||
if messageId.peerId.isGroupOrChannel && item.message.author != nil {
|
||||
case let .replyThread(replyThreadMessage):
|
||||
if replyThreadMessage.messageId.peerId != item.context.account.peerId {
|
||||
if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil {
|
||||
var isBroadcastChannel = false
|
||||
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
if isChannelPost, messageId == item.message.id {
|
||||
if replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -746,7 +746,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
@ -1359,6 +1359,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
return
|
||||
}
|
||||
|
||||
if let selectionState = item.controllerInteraction.selectionState {
|
||||
var selected = false
|
||||
var incoming = true
|
||||
|
@ -837,13 +837,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
var hasAvatar = false
|
||||
|
||||
var allowFullWidth = false
|
||||
let chatLocationPeerId: PeerId
|
||||
switch item.chatLocation {
|
||||
case let .peer(peerId):
|
||||
chatLocationPeerId = peerId
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
chatLocationPeerId = messageId.peerId
|
||||
}
|
||||
let chatLocationPeerId: PeerId = item.chatLocation.peerId
|
||||
|
||||
do {
|
||||
let peerId = chatLocationPeerId
|
||||
@ -885,7 +879,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
allowFullWidth = true
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, isChannelPost, _, _, _) = item.chatLocation, isChannelPost, messageId == firstMessage.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == firstMessage.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -1048,7 +1042,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
inlineBotNameString = attribute.title
|
||||
}
|
||||
} else if let attribute = attribute as? ReplyMessageAttribute {
|
||||
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == attribute.messageId {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == attribute.messageId {
|
||||
} else {
|
||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||
}
|
||||
@ -2953,6 +2947,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
default:
|
||||
break
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
canHaveSelection = false
|
||||
}
|
||||
|
||||
if let selectionState = item.controllerInteraction.selectionState, canHaveSelection {
|
||||
var selected = false
|
||||
|
@ -7,6 +7,7 @@ import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
import TelegramPresentationData
|
||||
import RadialStatusNode
|
||||
|
||||
final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
||||
private let separatorNode: ASDisplayNode
|
||||
@ -17,6 +18,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let avatarsNode: MergedAvatarsNode
|
||||
private let unreadIconNode: ASImageNode
|
||||
private var statusNode: RadialStatusNode?
|
||||
|
||||
required init() {
|
||||
self.separatorNode = ASDisplayNode()
|
||||
@ -257,6 +259,32 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
strongSelf.iconNode.isHidden = !replyPeers.isEmpty
|
||||
|
||||
let hasActivity = item.controllerInteraction.currentMessageWithLoadingReplyThread == item.message.id
|
||||
|
||||
if hasActivity {
|
||||
strongSelf.arrowNode.isHidden = true
|
||||
let statusNode: RadialStatusNode
|
||||
if let current = strongSelf.statusNode {
|
||||
statusNode = current
|
||||
} else {
|
||||
statusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
||||
strongSelf.statusNode = statusNode
|
||||
strongSelf.buttonNode.addSubnode(statusNode)
|
||||
}
|
||||
let statusSize = CGSize(width: 20.0, height: 20.0)
|
||||
statusNode.frame = CGRect(origin: CGPoint(x: boundingWidth - statusSize.width - 11.0, y: 8.0 + topOffset), size: statusSize)
|
||||
statusNode.transitionToState(.progress(color: messageTheme.accentTextColor, lineWidth: 1.5, value: nil, cancelEnabled: false), animated: false, synchronous: false, completion: {})
|
||||
} else {
|
||||
strongSelf.arrowNode.isHidden = false
|
||||
if let statusNode = strongSelf.statusNode {
|
||||
strongSelf.statusNode = nil
|
||||
statusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak statusNode] _ in
|
||||
statusNode?.removeFromSupernode()
|
||||
})
|
||||
strongSelf.arrowNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
let avatarsFrame = CGRect(origin: CGPoint(x: 13.0, y: 3.0 + topOffset), size: CGSize(width: imageSize * 3.0, height: imageSize))
|
||||
strongSelf.avatarsNode.frame = avatarsFrame
|
||||
strongSelf.avatarsNode.updateLayout(size: avatarsFrame.size)
|
||||
|
@ -182,8 +182,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
switch item.chatLocation {
|
||||
case let .peer(peerId):
|
||||
messagePeerId = peerId
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
messagePeerId = messageId.peerId
|
||||
case let .replyThread(replyThreadMessage):
|
||||
messagePeerId = replyThreadMessage.messageId.peerId
|
||||
}
|
||||
|
||||
do {
|
||||
@ -194,7 +194,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, isChannelPost, _, _, _) = item.chatLocation, isChannelPost, messageId == item.message.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -341,7 +341,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
@ -854,6 +854,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
return
|
||||
}
|
||||
|
||||
if let selectionState = item.controllerInteraction.selectionState {
|
||||
var selected = false
|
||||
var incoming = true
|
||||
|
@ -276,13 +276,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
||||
var effectiveAuthor: Peer?
|
||||
let displayAuthorInfo: Bool
|
||||
|
||||
let messagePeerId: PeerId
|
||||
switch chatLocation {
|
||||
case let .peer(peerId):
|
||||
messagePeerId = peerId
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
messagePeerId = messageId.peerId
|
||||
}
|
||||
let messagePeerId: PeerId = chatLocation.peerId
|
||||
|
||||
do {
|
||||
let peerId = messagePeerId
|
||||
@ -333,7 +327,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
||||
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
} else if case let .replyThread(messageId, isChannelPost, _, _, _) = chatLocation, isChannelPost, messageId == message.id {
|
||||
} else if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
if !hasActionMedia && !isBroadcastChannel {
|
||||
|
@ -249,15 +249,15 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
} else if incoming {
|
||||
hasAvatar = true
|
||||
}
|
||||
case let .replyThread(messageId, isChannelPost, _, _, _):
|
||||
if messageId.peerId != item.context.account.peerId {
|
||||
if messageId.peerId.isGroupOrChannel && item.message.author != nil {
|
||||
case let .replyThread(replyThreadMessage):
|
||||
if replyThreadMessage.messageId.peerId != item.context.account.peerId {
|
||||
if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil {
|
||||
var isBroadcastChannel = false
|
||||
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
if isChannelPost, messageId == item.message.id {
|
||||
if replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -415,7 +415,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
@ -920,6 +920,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
return
|
||||
}
|
||||
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
var isEmoji = false
|
||||
if let item = self.item, item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {
|
||||
|
@ -817,9 +817,9 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(messageId)))
|
||||
}
|
||||
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
|
||||
case let .replyThreadMessage(replyThreadMessage, messageId):
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId), subject: .message(messageId)))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadMessage), subject: .message(messageId)))
|
||||
}
|
||||
case let .stickerPack(name):
|
||||
let packReference: StickerPackReference = .name(name)
|
||||
|
@ -792,8 +792,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextAnonymousPlaceholder
|
||||
} else if case let .replyThread(_, isChannelPost, _, _, _) = interfaceState.chatLocation {
|
||||
if isChannelPost {
|
||||
} else if case let .replyThread(replyThreadMessage) = interfaceState.chatLocation {
|
||||
if replyThreadMessage.isChannelPost {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderComment
|
||||
} else {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderReply
|
||||
|
@ -125,8 +125,8 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
||||
if message.id.peerId == peerId {
|
||||
return true
|
||||
}
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
if message.id.peerId == messageId.peerId {
|
||||
case let .replyThread(replyThreadMessage):
|
||||
if message.id.peerId == replyThreadMessage.messageId.peerId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -90,11 +90,11 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
navigationController?.pushViewController(controller)
|
||||
case let .channelMessage(peerId, messageId):
|
||||
openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId), peekData: nil))
|
||||
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
|
||||
case let .replyThreadMessage(replyThreadMessage, messageId):
|
||||
if let navigationController = navigationController {
|
||||
ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in
|
||||
present(c, a)
|
||||
}, messageId: replyThreadMessageId, isChannelPost: isChannelPost, atMessage: messageId)
|
||||
}, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true)
|
||||
}
|
||||
case let .stickerPack(name):
|
||||
dismissInput()
|
||||
|
@ -59,11 +59,11 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(messageId)))
|
||||
}
|
||||
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
|
||||
case let .replyThreadMessage(replyThreadMessage, messageId):
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}, messageId: replyThreadMessageId, isChannelPost: isChannelPost, atMessage: messageId)
|
||||
}, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true)
|
||||
}
|
||||
case let .stickerPack(name):
|
||||
let packReference: StickerPackReference = .name(name)
|
||||
|
@ -322,11 +322,15 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
|
||||
case let .replyThread(id, replyId):
|
||||
let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)
|
||||
return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> ResolvedUrl? in
|
||||
guard let result = result else {
|
||||
return .channelMessage(peerId: peer.id, messageId: replyThreadMessageId)
|
||||
}
|
||||
return .replyThreadMessage(replyThreadMessageId: result.messageId, isChannelPost: true, maxMessage: result.maxMessage, maxReadIncomingMessageId: result.maxReadIncomingMessageId, maxReadOutgoingMessageId: result.maxReadIncomingMessageId, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))
|
||||
return .replyThreadMessage(replyThreadMessage: result, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -368,11 +372,15 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
|
||||
if let threadId = threadId {
|
||||
let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId)
|
||||
return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> ResolvedUrl? in
|
||||
guard let result = result else {
|
||||
return .channelMessage(peerId: foundPeer.id, messageId: replyThreadMessageId)
|
||||
}
|
||||
return .replyThreadMessage(replyThreadMessageId: result.messageId, isChannelPost: true, maxMessage: result.maxMessage, maxReadIncomingMessageId: result.maxReadIncomingMessageId, maxReadOutgoingMessageId: result.maxReadIncomingMessageId, messageId: messageId)
|
||||
return .replyThreadMessage(replyThreadMessage: result, messageId: messageId)
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(foundPeer.id, .chat(textInputState: nil, subject: .message(messageId), peekData: nil)))
|
||||
|
2
third-party/webrtc/webrtc-ios
vendored
2
third-party/webrtc/webrtc-ios
vendored
@ -1 +1 @@
|
||||
Subproject commit e4d49e73cd8206518e7b3dd75d54af0f0ef5b810
|
||||
Subproject commit 11255bcfff3180210a012f368e2d2bcd169b6877
|
Loading…
x
Reference in New Issue
Block a user