mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Comments update [skip ci]
This commit is contained in:
parent
c378d634c5
commit
0eccec10ed
@ -5790,3 +5790,7 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Conversation.TitleComments_1" = "%@ Comment";
|
||||
"Conversation.TitleComments_any" = "%@ Comments";
|
||||
"Conversation.TitleNoComments" = "Comments";
|
||||
|
||||
"Conversation.ContextMenuBlock" = "Block User";
|
||||
|
||||
"Replies.BlockAndDeleteRepliesActionTitle" = "Block and Delete Replies";
|
||||
|
@ -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, maxReadMessageId: MessageId?, messageId: MessageId)
|
||||
case replyThreadMessage(replyThreadMessageId: MessageId, isChannelPost: Bool, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, 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, maxReadMessageId: MessageId?)
|
||||
case replyThread(threadMessageId: MessageId, isChannelPost: Bool, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?)
|
||||
}
|
||||
|
||||
public final class NavigateToChatControllerParams {
|
||||
@ -674,5 +674,6 @@ public protocol AccountContext: class {
|
||||
func getStoredSecureIdPassword() -> String?
|
||||
|
||||
func chatLocationInput(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>) -> ChatLocationInput
|
||||
func chatLocationOutgoingReadState(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<MessageId?, NoError>
|
||||
func applyMaxReadIndex(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>, messageIndex: MessageIndex)
|
||||
}
|
||||
|
@ -985,6 +985,9 @@ public final class ChatListNode: ListView {
|
||||
var cachedResult: [PeerId: [(Peer, PeerInputActivity)]] = [:]
|
||||
previousPeerCache.with { dict -> Void in
|
||||
for (chatPeerId, activities) in activitiesByPeerId {
|
||||
if chatPeerId.threadId != nil {
|
||||
continue
|
||||
}
|
||||
var cachedChatResult: [(Peer, PeerInputActivity)] = []
|
||||
for (peerId, activity) in activities {
|
||||
if let peer = dict[peerId] {
|
||||
@ -993,7 +996,7 @@ public final class ChatListNode: ListView {
|
||||
foundAllPeers = false
|
||||
break
|
||||
}
|
||||
cachedResult[chatPeerId] = cachedChatResult
|
||||
cachedResult[chatPeerId.peerId] = cachedChatResult
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1004,6 +1007,9 @@ public final class ChatListNode: ListView {
|
||||
var result: [PeerId: [(Peer, PeerInputActivity)]] = [:]
|
||||
var peerCache: [PeerId: Peer] = [:]
|
||||
for (chatPeerId, activities) in activitiesByPeerId {
|
||||
if chatPeerId.threadId != nil {
|
||||
continue
|
||||
}
|
||||
var chatResult: [(Peer, PeerInputActivity)] = []
|
||||
|
||||
for (peerId, activity) in activities {
|
||||
@ -1013,7 +1019,7 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
|
||||
result[chatPeerId] = chatResult
|
||||
result[chatPeerId.peerId] = chatResult
|
||||
}
|
||||
let _ = previousPeerCache.swap(peerCache)
|
||||
return result
|
||||
|
@ -558,6 +558,10 @@ public final class Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
}
|
||||
|
||||
public func withUpdatedAttributes(_ attributes: [MessageAttribute]) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
}
|
||||
|
||||
func withUpdatedAssociatedMessages(_ associatedMessages: SimpleDictionary<MessageId, Message>) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
}
|
||||
|
@ -465,6 +465,11 @@ final class MessageHistoryTable: Table {
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
func removeAllMessagesWithForwardAuthor(peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], forEachMedia: (Media) -> Void) {
|
||||
let indices = self.allIndicesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace)
|
||||
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
func updateMessage(_ id: MessageId, message: StoreMessage, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) {
|
||||
var operations: [MessageHistoryIndexOperation] = []
|
||||
self.messageHistoryIndexTable.updateMessage(id, message: self.internalStoreMessages([message]).first!, operations: &operations)
|
||||
@ -2071,6 +2076,33 @@ final class MessageHistoryTable: Table {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func extractIntermediateEntryForwardAuthor(value: ReadBuffer) -> PeerId? {
|
||||
var type: Int8 = 0
|
||||
value.read(&type, offset: 0, length: 1)
|
||||
if type == 0 {
|
||||
value.skip(4) // stableId
|
||||
value.skip(4) // stableVersion
|
||||
|
||||
var hasGloballyUniqueId: Int8 = 0
|
||||
value.read(&hasGloballyUniqueId, offset: 0, length: 1)
|
||||
if hasGloballyUniqueId != 0 {
|
||||
value.skip(8) // globallyUniqueId
|
||||
}
|
||||
|
||||
value.skip(4) // flags
|
||||
value.skip(4) // tags
|
||||
|
||||
var forwardInfoFlags: Int8 = 0
|
||||
value.read(&forwardInfoFlags, offset: 0, length: 1)
|
||||
if forwardInfoFlags != 0 {
|
||||
var forwardAuthorId: Int64 = 0
|
||||
value.read(&forwardAuthorId, offset: 0, length: 8)
|
||||
return PeerId(forwardAuthorId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func readIntermediateEntry(_ key: ValueBoxKey, value: ReadBuffer) -> IntermediateMessageHistoryEntry {
|
||||
let index = extractKey(key)
|
||||
|
||||
@ -2654,6 +2686,17 @@ final class MessageHistoryTable: Table {
|
||||
return indices
|
||||
}
|
||||
|
||||
func allIndicesWithForwardAuthor(peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace) -> [MessageIndex] {
|
||||
var indices: [MessageIndex] = []
|
||||
self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId, namespace: namespace), end: self.upperBound(peerId: peerId, namespace: namespace), values: { key, value in
|
||||
if extractIntermediateEntryForwardAuthor(value: value) == forwardAuthorId {
|
||||
indices.append(extractKey(key))
|
||||
}
|
||||
return true
|
||||
}, limit: 0)
|
||||
return indices
|
||||
}
|
||||
|
||||
func getMessageCountInRange(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags, lowerBound: MessageIndex, upperBound: MessageIndex) -> Int {
|
||||
return self.tagsTable.getMessageCountInRange(tag: tag, peerId: peerId, namespace: namespace, lowerBound: lowerBound, upperBound: upperBound)
|
||||
}
|
||||
|
@ -251,18 +251,21 @@ public struct MessageHistoryViewOrderStatistics: OptionSet {
|
||||
public final class MessageHistoryViewExternalInput: Equatable {
|
||||
public let peerId: PeerId
|
||||
public let threadId: Int64
|
||||
public let maxReadMessageId: MessageId?
|
||||
public let maxReadIncomingMessageId: MessageId?
|
||||
public let maxReadOutgoingMessageId: MessageId?
|
||||
public let holes: [MessageId.Namespace: IndexSet]
|
||||
|
||||
public init(
|
||||
peerId: PeerId,
|
||||
threadId: Int64,
|
||||
maxReadMessageId: MessageId?,
|
||||
maxReadIncomingMessageId: MessageId?,
|
||||
maxReadOutgoingMessageId: MessageId?,
|
||||
holes: [MessageId.Namespace: IndexSet]
|
||||
) {
|
||||
self.peerId = peerId
|
||||
self.threadId = threadId
|
||||
self.maxReadMessageId = maxReadMessageId
|
||||
self.maxReadIncomingMessageId = maxReadIncomingMessageId
|
||||
self.maxReadOutgoingMessageId = maxReadOutgoingMessageId
|
||||
self.holes = holes
|
||||
}
|
||||
|
||||
@ -279,6 +282,12 @@ public final class MessageHistoryViewExternalInput: Equatable {
|
||||
if lhs.holes != rhs.holes {
|
||||
return false
|
||||
}
|
||||
if lhs.maxReadIncomingMessageId != rhs.maxReadIncomingMessageId {
|
||||
return false
|
||||
}
|
||||
if lhs.maxReadOutgoingMessageId != rhs.maxReadOutgoingMessageId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1032,7 +1041,7 @@ public final class MessageHistoryView {
|
||||
self.maxReadIndex = nil
|
||||
}
|
||||
case let .external(input):
|
||||
if let maxReadMesageId = input.maxReadMessageId {
|
||||
if let maxReadMesageId = input.maxReadIncomingMessageId {
|
||||
var maxIndex: MessageIndex?
|
||||
|
||||
let hasUnread = true
|
||||
|
@ -122,6 +122,11 @@ public final class Transaction {
|
||||
self.postbox?.removeAllMessagesWithAuthor(peerId, authorId: authorId, namespace: namespace, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
public func removeAllMessagesWithForwardAuthor(_ peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.removeAllMessagesWithForwardAuthor(peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
public func messageIdsForGlobalIds(_ ids: [Int32]) -> [MessageId] {
|
||||
assert(!self.disposed)
|
||||
if let postbox = self.postbox {
|
||||
@ -1690,6 +1695,10 @@ public final class Postbox {
|
||||
self.messageHistoryTable.removeAllMessagesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
fileprivate func removeAllMessagesWithForwardAuthor(_ peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) {
|
||||
self.messageHistoryTable.removeAllMessagesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: ¤tUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, forEachMedia: forEachMedia)
|
||||
}
|
||||
|
||||
fileprivate func resetIncomingReadStates(_ states: [PeerId: [MessageId.Namespace: PeerReadState]]) {
|
||||
self.messageHistoryTable.resetIncomingReadStates(states, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations)
|
||||
}
|
||||
@ -2459,7 +2468,7 @@ public final class Postbox {
|
||||
}
|
||||
}
|
||||
case let .external(input):
|
||||
if let maxReadMessageId = input.maxReadMessageId {
|
||||
if let maxReadMessageId = input.maxReadIncomingMessageId {
|
||||
anchor = .message(maxReadMessageId)
|
||||
} else {
|
||||
anchor = .upperBound
|
||||
|
@ -128,3 +128,39 @@ public final class UnreadMessageCountsView: PostboxView {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
final class MutableCombinedReadStateView: MutablePostboxView {
|
||||
private let peerId: PeerId
|
||||
fileprivate var state: CombinedPeerReadState?
|
||||
|
||||
init(postbox: Postbox, peerId: PeerId) {
|
||||
self.peerId = peerId
|
||||
self.state = postbox.readStateTable.getCombinedState(peerId)
|
||||
}
|
||||
|
||||
func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool {
|
||||
var updated = false
|
||||
|
||||
if transaction.alteredInitialPeerCombinedReadStates[self.peerId] != nil {
|
||||
let state = postbox.readStateTable.getCombinedState(peerId)
|
||||
if state != self.state {
|
||||
self.state = state
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return CombinedReadStateView(self)
|
||||
}
|
||||
}
|
||||
|
||||
public final class CombinedReadStateView: PostboxView {
|
||||
public let state: CombinedPeerReadState?
|
||||
|
||||
init(_ view: MutableCombinedReadStateView) {
|
||||
self.state = view.state
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ public enum PostboxViewKey: Hashable {
|
||||
case historyTagSummaryView(tag: MessageTags, peerId: PeerId, namespace: MessageId.Namespace)
|
||||
case cachedPeerData(peerId: PeerId)
|
||||
case unreadCounts(items: [UnreadMessageCountsItem])
|
||||
case combinedReadState(peerId: PeerId)
|
||||
case peerNotificationSettings(peerIds: Set<PeerId>)
|
||||
case pendingPeerNotificationSettings
|
||||
case messageOfInterestHole(location: MessageOfInterestViewLocation, namespace: MessageId.Namespace, count: Int)
|
||||
@ -60,6 +61,8 @@ public enum PostboxViewKey: Hashable {
|
||||
return peerId.hashValue
|
||||
case .unreadCounts:
|
||||
return 5
|
||||
case .combinedReadState:
|
||||
return 16
|
||||
case .peerNotificationSettings:
|
||||
return 6
|
||||
case .pendingPeerNotificationSettings:
|
||||
@ -177,6 +180,12 @@ public enum PostboxViewKey: Hashable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .combinedReadState(peerId):
|
||||
if case .combinedReadState(peerId) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peerNotificationSettings(peerIds):
|
||||
if case .peerNotificationSettings(peerIds) = rhs {
|
||||
return true
|
||||
@ -295,6 +304,8 @@ func postboxViewForKey(postbox: Postbox, key: PostboxViewKey) -> MutablePostboxV
|
||||
return MutableCachedPeerDataView(postbox: postbox, peerId: peerId)
|
||||
case let .unreadCounts(items):
|
||||
return MutableUnreadMessageCountsView(postbox: postbox, items: items)
|
||||
case let .combinedReadState(peerId):
|
||||
return MutableCombinedReadStateView(postbox: postbox, peerId: peerId)
|
||||
case let .peerNotificationSettings(peerIds):
|
||||
return MutablePeerNotificationSettingsView(postbox: postbox, peerIds: peerIds)
|
||||
case .pendingPeerNotificationSettings:
|
||||
|
@ -255,8 +255,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[643940105] = { return Api.Update.parse_updatePhoneCallSignalingData($0) }
|
||||
dict[1708307556] = { return Api.Update.parse_updateChannelParticipant($0) }
|
||||
dict[1854571743] = { return Api.Update.parse_updateChannelMessageForwards($0) }
|
||||
dict[295679367] = { return Api.Update.parse_updateReadDiscussion($0) }
|
||||
dict[-966672061] = { return Api.Update.parse_updateReadChannelDiscussionInbox($0) }
|
||||
dict[1178116716] = { return Api.Update.parse_updateReadChannelDiscussionOutbox($0) }
|
||||
dict[610945826] = { return Api.Update.parse_updatePeerBlocked($0) }
|
||||
dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($0) }
|
||||
dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) }
|
||||
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
|
||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
|
||||
@ -305,6 +308,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
||||
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
||||
dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) }
|
||||
dict[-1199954735] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||
dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) }
|
||||
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
|
||||
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
|
||||
@ -435,7 +439,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-886477832] = { return Api.LabeledPrice.parse_labeledPrice($0) }
|
||||
dict[-438840932] = { return Api.messages.ChatFull.parse_chatFull($0) }
|
||||
dict[-618540889] = { return Api.InputSecureValue.parse_inputSecureValue($0) }
|
||||
dict[1835297038] = { return Api.messages.DiscussionMessage.parse_discussionMessage($0) }
|
||||
dict[-170029155] = { return Api.messages.DiscussionMessage.parse_discussionMessage($0) }
|
||||
dict[1722786150] = { return Api.help.DeepLinkInfo.parse_deepLinkInfoEmpty($0) }
|
||||
dict[1783556146] = { return Api.help.DeepLinkInfo.parse_deepLinkInfo($0) }
|
||||
dict[-313079300] = { return Api.account.WebAuthorizations.parse_webAuthorizations($0) }
|
||||
@ -451,6 +455,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) }
|
||||
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
|
||||
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
|
||||
dict[-1553558980] = { return Api.messages.MessageReactionsList.parse_messageReactionsList($0) }
|
||||
dict[-1014526429] = { return Api.help.Country.parse_country($0) }
|
||||
dict[418631927] = { return Api.StatsGroupTopPoster.parse_statsGroupTopPoster($0) }
|
||||
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
|
||||
@ -671,6 +676,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1834973166] = { return Api.BaseTheme.parse_baseThemeTinted($0) }
|
||||
dict[1527845466] = { return Api.BaseTheme.parse_baseThemeArctic($0) }
|
||||
dict[398898678] = { return Api.help.Support.parse_support($0) }
|
||||
dict[1873957073] = { return Api.ReactionCount.parse_reactionCount($0) }
|
||||
dict[1474492012] = { return Api.MessagesFilter.parse_inputMessagesFilterEmpty($0) }
|
||||
dict[-1777752804] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotos($0) }
|
||||
dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) }
|
||||
@ -699,6 +705,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) }
|
||||
dict[1984136919] = { return Api.wallet.LiteResponse.parse_liteResponse($0) }
|
||||
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
||||
dict[-764945220] = { return Api.MessageUserReaction.parse_messageUserReaction($0) }
|
||||
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
|
||||
dict[982505656] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
|
||||
dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) }
|
||||
@ -1052,6 +1059,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.FoundGifs:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReactions:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.FileLocation:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Poll:
|
||||
@ -1164,6 +1173,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.DraftMessage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.MessageReactionsList:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.Country:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StatsGroupTopPoster:
|
||||
@ -1388,6 +1399,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.Support:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ReactionCount:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessagesFilter:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.Dialogs:
|
||||
@ -1402,6 +1415,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.InviteText:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageUserReaction:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BotInlineMessage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputPeerNotifySettings:
|
||||
|
@ -825,13 +825,13 @@ public struct messages {
|
||||
|
||||
}
|
||||
public enum DiscussionMessage: TypeConstructorDescription {
|
||||
case discussionMessage(flags: Int32, messages: [Api.Message], maxId: Int32?, readMaxId: Int32?, chats: [Api.Chat], users: [Api.User])
|
||||
case discussionMessage(flags: Int32, messages: [Api.Message], maxId: Int32?, readInboxMaxId: Int32?, readOutboxMaxId: Int32?, chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .discussionMessage(let flags, let messages, let maxId, let readMaxId, let chats, let users):
|
||||
case .discussionMessage(let flags, let messages, let maxId, let readInboxMaxId, let readOutboxMaxId, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1835297038)
|
||||
buffer.appendInt32(-170029155)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
@ -840,7 +840,8 @@ public struct messages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(maxId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(readMaxId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(readInboxMaxId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(readOutboxMaxId!, buffer: buffer, boxed: false)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
@ -857,8 +858,8 @@ public struct messages {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .discussionMessage(let flags, let messages, let maxId, let readMaxId, let chats, let users):
|
||||
return ("discussionMessage", [("flags", flags), ("messages", messages), ("maxId", maxId), ("readMaxId", readMaxId), ("chats", chats), ("users", users)])
|
||||
case .discussionMessage(let flags, let messages, let maxId, let readInboxMaxId, let readOutboxMaxId, let chats, let users):
|
||||
return ("discussionMessage", [("flags", flags), ("messages", messages), ("maxId", maxId), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("chats", chats), ("users", users)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,22 +874,87 @@ public struct messages {
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
|
||||
var _5: [Api.Chat]?
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_5 = reader.readInt32() }
|
||||
var _6: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.messages.DiscussionMessage.discussionMessage(flags: _1!, messages: _2!, maxId: _3, readMaxId: _4, chats: _5!, users: _6!)
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.messages.DiscussionMessage.discussionMessage(flags: _1!, messages: _2!, maxId: _3, readInboxMaxId: _4, readOutboxMaxId: _5, chats: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum MessageReactionsList: TypeConstructorDescription {
|
||||
case messageReactionsList(flags: Int32, count: Int32, reactions: [Api.MessageUserReaction], users: [Api.User], nextOffset: String?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageReactionsList(let flags, let count, let reactions, let users, let nextOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1553558980)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(reactions.count))
|
||||
for item in reactions {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageReactionsList(let flags, let count, let reactions, let users, let nextOffset):
|
||||
return ("messageReactionsList", [("flags", flags), ("count", count), ("reactions", reactions), ("users", users), ("nextOffset", nextOffset)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageReactionsList(_ reader: BufferReader) -> MessageReactionsList? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.MessageUserReaction]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserReaction.self)
|
||||
}
|
||||
var _4: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _5: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.messages.MessageReactionsList.messageReactionsList(flags: _1!, count: _2!, reactions: _3!, users: _4!, nextOffset: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -6170,8 +6236,11 @@ public extension Api {
|
||||
case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer)
|
||||
case updateChannelParticipant(flags: Int32, channelId: Int32, date: Int32, userId: Int32, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, qts: Int32)
|
||||
case updateChannelMessageForwards(channelId: Int32, id: Int32, forwards: Int32)
|
||||
case updateReadDiscussion(peer: Api.Peer, msgId: Int32, readMaxId: Int32)
|
||||
case updateReadChannelDiscussionInbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32)
|
||||
case updateReadChannelDiscussionOutbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32)
|
||||
case updatePeerBlocked(peerId: Api.Peer, blocked: Api.Bool)
|
||||
case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction)
|
||||
case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -6865,12 +6934,20 @@ public extension Api {
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(forwards, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateReadDiscussion(let peer, let msgId, let readMaxId):
|
||||
case .updateReadChannelDiscussionInbox(let channelId, let topMsgId, let readMaxId):
|
||||
if boxed {
|
||||
buffer.appendInt32(295679367)
|
||||
buffer.appendInt32(-966672061)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt32(topMsgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(readMaxId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateReadChannelDiscussionOutbox(let channelId, let topMsgId, let readMaxId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1178116716)
|
||||
}
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt32(topMsgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(readMaxId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updatePeerBlocked(let peerId, let blocked):
|
||||
@ -6880,6 +6957,24 @@ public extension Api {
|
||||
peerId.serialize(buffer, true)
|
||||
blocked.serialize(buffer, true)
|
||||
break
|
||||
case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action):
|
||||
if boxed {
|
||||
buffer.appendInt32(-13975905)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(userId, buffer: buffer, boxed: false)
|
||||
action.serialize(buffer, true)
|
||||
break
|
||||
case .updateMessageReactions(let peer, let msgId, let reactions):
|
||||
if boxed {
|
||||
buffer.appendInt32(357013699)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
reactions.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -7049,10 +7144,16 @@ public extension Api {
|
||||
return ("updateChannelParticipant", [("flags", flags), ("channelId", channelId), ("date", date), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("qts", qts)])
|
||||
case .updateChannelMessageForwards(let channelId, let id, let forwards):
|
||||
return ("updateChannelMessageForwards", [("channelId", channelId), ("id", id), ("forwards", forwards)])
|
||||
case .updateReadDiscussion(let peer, let msgId, let readMaxId):
|
||||
return ("updateReadDiscussion", [("peer", peer), ("msgId", msgId), ("readMaxId", readMaxId)])
|
||||
case .updateReadChannelDiscussionInbox(let channelId, let topMsgId, let readMaxId):
|
||||
return ("updateReadChannelDiscussionInbox", [("channelId", channelId), ("topMsgId", topMsgId), ("readMaxId", readMaxId)])
|
||||
case .updateReadChannelDiscussionOutbox(let channelId, let topMsgId, let readMaxId):
|
||||
return ("updateReadChannelDiscussionOutbox", [("channelId", channelId), ("topMsgId", topMsgId), ("readMaxId", readMaxId)])
|
||||
case .updatePeerBlocked(let peerId, let blocked):
|
||||
return ("updatePeerBlocked", [("peerId", peerId), ("blocked", blocked)])
|
||||
case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action):
|
||||
return ("updateChannelUserTyping", [("flags", flags), ("channelId", channelId), ("topMsgId", topMsgId), ("userId", userId), ("action", action)])
|
||||
case .updateMessageReactions(let peer, let msgId, let reactions):
|
||||
return ("updateMessageReactions", [("peer", peer), ("msgId", msgId), ("reactions", reactions)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -8432,11 +8533,9 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateReadDiscussion(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
public static func parse_updateReadChannelDiscussionInbox(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
@ -8445,7 +8544,24 @@ public extension Api {
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateReadDiscussion(peer: _1!, msgId: _2!, readMaxId: _3!)
|
||||
return Api.Update.updateReadChannelDiscussionInbox(channelId: _1!, topMsgId: _2!, readMaxId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateReadChannelDiscussionOutbox(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateReadChannelDiscussionOutbox(channelId: _1!, topMsgId: _2!, readMaxId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -8469,6 +8585,52 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateChannelUserTyping(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Api.SendMessageAction?
|
||||
if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.SendMessageAction
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.Update.updateChannelUserTyping(flags: _1!, channelId: _2!, topMsgId: _3, userId: _4!, action: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateMessageReactions(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.MessageReactions?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.MessageReactions
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateMessageReactions(peer: _1!, msgId: _2!, reactions: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum PopularContact: TypeConstructorDescription {
|
||||
@ -9648,6 +9810,50 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum MessageReactions: TypeConstructorDescription {
|
||||
case messageReactions(flags: Int32, results: [Api.ReactionCount])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageReactions(let flags, let results):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1199954735)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(results.count))
|
||||
for item in results {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageReactions(let flags, let results):
|
||||
return ("messageReactions", [("flags", flags), ("results", results)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageReactions(_ reader: BufferReader) -> MessageReactions? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: [Api.ReactionCount]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageReactions.messageReactions(flags: _1!, results: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum FileLocation: TypeConstructorDescription {
|
||||
case fileLocationToBeDeprecated(volumeId: Int64, localId: Int32)
|
||||
@ -19462,6 +19668,48 @@ public extension Api {
|
||||
return Api.BaseTheme.baseThemeArctic
|
||||
}
|
||||
|
||||
}
|
||||
public enum ReactionCount: TypeConstructorDescription {
|
||||
case reactionCount(flags: Int32, reaction: String, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .reactionCount(let flags, let reaction, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(1873957073)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(reaction, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .reactionCount(let flags, let reaction, let count):
|
||||
return ("reactionCount", [("flags", flags), ("reaction", reaction), ("count", count)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_reactionCount(_ reader: BufferReader) -> ReactionCount? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.ReactionCount.reactionCount(flags: _1!, reaction: _2!, count: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum MessagesFilter: TypeConstructorDescription {
|
||||
case inputMessagesFilterEmpty
|
||||
@ -19774,6 +20022,44 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum MessageUserReaction: TypeConstructorDescription {
|
||||
case messageUserReaction(userId: Int32, reaction: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageUserReaction(let userId, let reaction):
|
||||
if boxed {
|
||||
buffer.appendInt32(-764945220)
|
||||
}
|
||||
serializeInt32(userId, buffer: buffer, boxed: false)
|
||||
serializeString(reaction, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageUserReaction(let userId, let reaction):
|
||||
return ("messageUserReaction", [("userId", userId), ("reaction", reaction)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageUserReaction(_ reader: BufferReader) -> MessageUserReaction? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageUserReaction.messageUserReaction(userId: _1!, reaction: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum BotInlineMessage: TypeConstructorDescription {
|
||||
case botInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
|
||||
|
@ -1784,21 +1784,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func setTyping(peer: Api.InputPeer, action: Api.SendMessageAction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1551737264)
|
||||
peer.serialize(buffer, true)
|
||||
action.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.setTyping", parameters: [("peer", peer), ("action", action)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func reportSpam(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-820669733)
|
||||
@ -3777,6 +3762,78 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func setTyping(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, action: Api.SendMessageAction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1486110434)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
action.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.setTyping", parameters: [("flags", flags), ("peer", peer), ("topMsgId", topMsgId), ("action", action)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(627641572)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(reaction!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.sendReaction", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("reaction", reaction)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getMessagesReactions(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1950707482)
|
||||
peer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
return (FunctionDescription(name: "messages.getMessagesReactions", parameters: [("peer", peer), ("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getMessageReactionsList(flags: Int32, peer: Api.InputPeer, id: Int32, reaction: String?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.MessageReactionsList>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-521245833)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(reaction!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.getMessageReactionsList", parameters: [("flags", flags), ("peer", peer), ("id", id), ("reaction", reaction), ("offset", offset), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.MessageReactionsList? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.MessageReactionsList?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.MessageReactionsList
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct channels {
|
||||
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
|
@ -1186,17 +1186,17 @@ public class Account {
|
||||
return self.viewTracker.chatHistoryPreloadManager.addAdditionalPeerId(peerId: peerId)
|
||||
}
|
||||
|
||||
public func peerInputActivities(peerId: PeerId) -> Signal<[(PeerId, PeerInputActivity)], NoError> {
|
||||
public func peerInputActivities(peerId: PeerActivitySpace) -> Signal<[(PeerId, PeerInputActivity)], NoError> {
|
||||
return self.peerInputActivityManager.activities(peerId: peerId)
|
||||
|> map { activities in
|
||||
return activities.map({ ($0.0, $0.1.activity) })
|
||||
}
|
||||
}
|
||||
|
||||
public func allPeerInputActivities() -> Signal<[PeerId: [PeerId: PeerInputActivity]], NoError> {
|
||||
public func allPeerInputActivities() -> Signal<[PeerActivitySpace: [PeerId: PeerInputActivity]], NoError> {
|
||||
return self.peerInputActivityManager.allActivities()
|
||||
|> map { activities in
|
||||
var result: [PeerId: [PeerId: PeerInputActivity]] = [:]
|
||||
var result: [PeerActivitySpace: [PeerId: PeerInputActivity]] = [:]
|
||||
for (chatPeerId, chatActivities) in activities {
|
||||
result[chatPeerId] = chatActivities.mapValues({ $0.activity })
|
||||
}
|
||||
@ -1204,7 +1204,7 @@ public class Account {
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLocalInputActivity(peerId: PeerId, activity: PeerInputActivity, isPresent: Bool) {
|
||||
public func updateLocalInputActivity(peerId: PeerActivitySpace, activity: PeerInputActivity, isPresent: Bool) {
|
||||
self.localInputActivityManager.transaction { manager in
|
||||
if isPresent {
|
||||
manager.addActivity(chatPeerId: peerId, peerId: self.peerId, activity: activity)
|
||||
@ -1214,7 +1214,7 @@ public class Account {
|
||||
}
|
||||
}
|
||||
|
||||
public func acquireLocalInputActivity(peerId: PeerId, activity: PeerInputActivity) -> Disposable {
|
||||
public func acquireLocalInputActivity(peerId: PeerActivitySpace, activity: PeerInputActivity) -> Disposable {
|
||||
return self.localInputActivityManager.acquireActivity(chatPeerId: peerId, peerId: self.peerId, activity: activity)
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32)
|
||||
case AddSecretMessages([Api.EncryptedMessage])
|
||||
case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32)
|
||||
case AddPeerInputActivity(chatPeerId: PeerId, peerId: PeerId?, activity: PeerInputActivity?)
|
||||
case AddPeerInputActivity(chatPeerId: PeerActivitySpace, peerId: PeerId?, activity: PeerInputActivity?)
|
||||
case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation)
|
||||
case ReadMessageContents((PeerId?, [Int32]))
|
||||
case UpdateMessageImpressionCount(MessageId, Int32)
|
||||
@ -108,6 +108,7 @@ enum AccountStateMutationOperation {
|
||||
case SyncChatListFilters
|
||||
case UpdateChatListFilterOrder(order: [Int32])
|
||||
case UpdateChatListFilter(id: Int32, filter: Api.DialogFilter?)
|
||||
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool)
|
||||
}
|
||||
|
||||
struct HoleFromPreviousState {
|
||||
@ -270,6 +271,10 @@ struct AccountMutableState {
|
||||
self.addOperation(.ReadOutbox(messageId, timestamp))
|
||||
}
|
||||
|
||||
mutating func readThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool) {
|
||||
self.addOperation(.UpdateReadThread(threadMessageId: threadMessageId, readMaxId: readMaxId, isIncoming: isIncoming))
|
||||
}
|
||||
|
||||
mutating func readGroupFeedInbox(groupId: PeerGroupId, index: MessageIndex) {
|
||||
self.addOperation(.ReadGroupFeedInbox(groupId, index))
|
||||
}
|
||||
@ -420,7 +425,7 @@ struct AccountMutableState {
|
||||
self.addOperation(.ReadSecretOutbox(peerId: peerId, maxTimestamp: timestamp, actionTimestamp: actionTimestamp))
|
||||
}
|
||||
|
||||
mutating func addPeerInputActivity(chatPeerId: PeerId, peerId: PeerId?, activity: PeerInputActivity?) {
|
||||
mutating func addPeerInputActivity(chatPeerId: PeerActivitySpace, peerId: PeerId?, activity: PeerInputActivity?) {
|
||||
self.addOperation(.AddPeerInputActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity))
|
||||
}
|
||||
|
||||
@ -474,7 +479,7 @@ struct AccountMutableState {
|
||||
|
||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread:
|
||||
break
|
||||
case let .AddMessages(messages, location):
|
||||
for message in messages {
|
||||
@ -587,19 +592,21 @@ struct AccountReplayedFinalState {
|
||||
let addedIncomingMessageIds: [MessageId]
|
||||
let wasScheduledMessageIds: [MessageId]
|
||||
let addedSecretMessageIds: [MessageId]
|
||||
let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]]
|
||||
let updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]]
|
||||
let updatedWebpages: [MediaId: TelegramMediaWebpage]
|
||||
let updatedCalls: [Api.PhoneCall]
|
||||
let addedCallSignalingData: [(Int64, Data)]
|
||||
let updatedPeersNearby: [PeerNearby]?
|
||||
let isContactUpdates: [(PeerId, Bool)]
|
||||
let delayNotificatonsUntil: Int32?
|
||||
let updatedIncomingThreadReadStates: [MessageId: MessageId.Id]
|
||||
let updatedOutgoingThreadReadStates: [MessageId: MessageId.Id]
|
||||
}
|
||||
|
||||
struct AccountFinalStateEvents {
|
||||
let addedIncomingMessageIds: [MessageId]
|
||||
let wasScheduledMessageIds:[MessageId]
|
||||
let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]]
|
||||
let updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]]
|
||||
let updatedWebpages: [MediaId: TelegramMediaWebpage]
|
||||
let updatedCalls: [Api.PhoneCall]
|
||||
let addedCallSignalingData: [(Int64, Data)]
|
||||
@ -611,12 +618,14 @@ struct AccountFinalStateEvents {
|
||||
let updatedQts: Int32?
|
||||
let externallyUpdatedPeerId: Set<PeerId>
|
||||
let authorizationListUpdated: Bool
|
||||
let updatedIncomingThreadReadStates: [MessageId: MessageId.Id]
|
||||
let updatedOutgoingThreadReadStates: [MessageId: MessageId.Id]
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated
|
||||
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty
|
||||
}
|
||||
|
||||
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false) {
|
||||
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) {
|
||||
self.addedIncomingMessageIds = addedIncomingMessageIds
|
||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||
self.updatedTypingActivities = updatedTypingActivities
|
||||
@ -631,6 +640,8 @@ struct AccountFinalStateEvents {
|
||||
self.updatedQts = updatedQts
|
||||
self.externallyUpdatedPeerId = externallyUpdatedPeerId
|
||||
self.authorizationListUpdated = authorizationListUpdated
|
||||
self.updatedIncomingThreadReadStates = updatedIncomingThreadReadStates
|
||||
self.updatedOutgoingThreadReadStates = updatedOutgoingThreadReadStates
|
||||
}
|
||||
|
||||
init(state: AccountReplayedFinalState) {
|
||||
@ -648,6 +659,8 @@ struct AccountFinalStateEvents {
|
||||
self.updatedQts = state.state.state.updatedQts
|
||||
self.externallyUpdatedPeerId = state.state.state.externallyUpdatedPeerId
|
||||
self.authorizationListUpdated = state.state.state.authorizationListUpdated
|
||||
self.updatedIncomingThreadReadStates = state.updatedIncomingThreadReadStates
|
||||
self.updatedOutgoingThreadReadStates = state.updatedOutgoingThreadReadStates
|
||||
}
|
||||
|
||||
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
||||
@ -673,6 +686,6 @@ struct AccountFinalStateEvents {
|
||||
let externallyUpdatedPeerId = self.externallyUpdatedPeerId.union(other.externallyUpdatedPeerId)
|
||||
let authorizationListUpdated = self.authorizationListUpdated || other.authorizationListUpdated
|
||||
|
||||
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated)
|
||||
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }))
|
||||
}
|
||||
}
|
||||
|
@ -1206,16 +1206,21 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
||||
updatedState.readSecretOutbox(peerId: PeerId(namespace: Namespaces.Peer.SecretChat, id: chatId), timestamp: maxDate, actionTimestamp: date)
|
||||
case let .updateUserTyping(userId, type):
|
||||
if let date = updatesDate, date + 60 > serverTime {
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: PeerInputActivity(apiType: type))
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), threadId: nil), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: PeerInputActivity(apiType: type))
|
||||
}
|
||||
case let .updateChatUserTyping(chatId, userId, type):
|
||||
if let date = updatesDate, date + 60 > serverTime {
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: PeerInputActivity(apiType: type))
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: chatId), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: PeerInputActivity(apiType: type))
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId), threadId: nil), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: PeerInputActivity(apiType: type))
|
||||
}
|
||||
case let .updateChannelUserTyping(_, channelId, topMsgId, userId, type):
|
||||
if let date = updatesDate, date + 60 > serverTime {
|
||||
let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||
let threadId = topMsgId.flatMap { makeMessageThreadId(MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: $0)) }
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: channelPeerId, threadId: threadId), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: PeerInputActivity(apiType: type))
|
||||
}
|
||||
case let .updateEncryptedChatTyping(chatId):
|
||||
if let date = updatesDate, date + 60 > serverTime {
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerId(namespace: Namespaces.Peer.SecretChat, id: chatId), peerId: nil, activity: .typingText)
|
||||
updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: PeerId(namespace: Namespaces.Peer.SecretChat, id: chatId), threadId: nil), peerId: nil, activity: .typingText)
|
||||
}
|
||||
case let .updateDialogPinned(flags, folderId, peer):
|
||||
let groupId: PeerGroupId = folderId.flatMap(PeerGroupId.init(rawValue:)) ?? .root
|
||||
@ -2094,7 +2099,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
||||
var currentAddScheduledMessages: OptimizeAddMessagesState?
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread:
|
||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||
}
|
||||
@ -2174,7 +2179,9 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
|
||||
var peerIdsWithAddedSecretMessages = Set<PeerId>()
|
||||
|
||||
var updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:]
|
||||
var updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:]
|
||||
var updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:]
|
||||
var updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]
|
||||
var updatedSecretChatTypingActivities = Set<PeerId>()
|
||||
var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:]
|
||||
var updatedCalls: [Api.PhoneCall] = []
|
||||
@ -2318,10 +2325,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
let chatPeerId = message.id.peerId
|
||||
if let authorId = message.authorId {
|
||||
let activityValue: PeerInputActivity? = nil
|
||||
if updatedTypingActivities[chatPeerId] == nil {
|
||||
updatedTypingActivities[chatPeerId] = [authorId: activityValue]
|
||||
if updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)] == nil {
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)] = [authorId: activityValue]
|
||||
} else {
|
||||
updatedTypingActivities[chatPeerId]![authorId] = activityValue
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)]![authorId] = activityValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -2528,6 +2535,24 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
case let .ReadGroupFeedInbox(groupId, index):
|
||||
break
|
||||
//transaction.applyGroupFeedReadMaxIndex(groupId: groupId, index: index)
|
||||
case let .UpdateReadThread(threadMessageId, readMaxId, isIncoming):
|
||||
if isIncoming {
|
||||
if let currentId = updatedIncomingThreadReadStates[threadMessageId] {
|
||||
if currentId < readMaxId {
|
||||
updatedIncomingThreadReadStates[threadMessageId] = readMaxId
|
||||
}
|
||||
} else {
|
||||
updatedIncomingThreadReadStates[threadMessageId] = readMaxId
|
||||
}
|
||||
} else {
|
||||
if let currentId = updatedOutgoingThreadReadStates[threadMessageId] {
|
||||
if currentId < readMaxId {
|
||||
updatedOutgoingThreadReadStates[threadMessageId] = readMaxId
|
||||
}
|
||||
} else {
|
||||
updatedOutgoingThreadReadStates[threadMessageId] = readMaxId
|
||||
}
|
||||
}
|
||||
case let .ResetReadState(peerId, namespace, maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread):
|
||||
var markedUnreadValue: Bool = false
|
||||
if let markedUnread = markedUnread {
|
||||
@ -2750,8 +2775,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
} else {
|
||||
updatedTypingActivities[chatPeerId]![peerId] = activity
|
||||
}
|
||||
} else if chatPeerId.namespace == Namespaces.Peer.SecretChat {
|
||||
updatedSecretChatTypingActivities.insert(chatPeerId)
|
||||
} else if chatPeerId.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
updatedSecretChatTypingActivities.insert(chatPeerId.peerId)
|
||||
}
|
||||
case let .UpdatePinnedItemIds(groupId, pinnedOperation):
|
||||
switch pinnedOperation {
|
||||
@ -3132,10 +3157,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
if let peer = transaction.getPeer(chatPeerId) as? TelegramSecretChat {
|
||||
let authorId = peer.regularPeerId
|
||||
let activityValue: PeerInputActivity? = .typingText
|
||||
if updatedTypingActivities[chatPeerId] == nil {
|
||||
updatedTypingActivities[chatPeerId] = [authorId: activityValue]
|
||||
if updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)] == nil {
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)] = [authorId: activityValue]
|
||||
} else {
|
||||
updatedTypingActivities[chatPeerId]![authorId] = activityValue
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)]![authorId] = activityValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3180,10 +3205,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
|
||||
for (chatPeerId, authorId) in addedSecretMessageAuthorIds {
|
||||
let activityValue: PeerInputActivity? = nil
|
||||
if updatedTypingActivities[chatPeerId] == nil {
|
||||
updatedTypingActivities[chatPeerId] = [authorId: activityValue]
|
||||
if updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)] == nil {
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)] = [authorId: activityValue]
|
||||
} else {
|
||||
updatedTypingActivities[chatPeerId]![authorId] = activityValue
|
||||
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)]![authorId] = activityValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -3260,5 +3285,5 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
requestChatListFiltersSync(transaction: transaction)
|
||||
}
|
||||
|
||||
return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil)
|
||||
return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates)
|
||||
}
|
||||
|
@ -138,6 +138,11 @@ public final class AccountStateManager {
|
||||
return self.authorizationListUpdatesPipe.signal()
|
||||
}
|
||||
|
||||
private let threadReadStateUpdatesPipe = ValuePipe<(incoming: [MessageId: MessageId.Id], outgoing: [MessageId: MessageId.Id])>()
|
||||
var threadReadStateUpdates: Signal<(incoming: [MessageId: MessageId.Id], outgoing: [MessageId: MessageId.Id]), NoError> {
|
||||
return self.threadReadStateUpdatesPipe.signal()
|
||||
}
|
||||
|
||||
private var updatedWebpageContexts: [MediaId: UpdatedWebpageSubscriberContext] = [:]
|
||||
private var updatedPeersNearbyContext = UpdatedPeersNearbySubscriberContext()
|
||||
|
||||
@ -658,6 +663,9 @@ public final class AccountStateManager {
|
||||
strongSelf.callSessionManager.addCallSignalingData(id: id, data: data)
|
||||
}
|
||||
}
|
||||
if !events.updatedIncomingThreadReadStates.isEmpty || !events.updatedOutgoingThreadReadStates.isEmpty {
|
||||
strongSelf.threadReadStateUpdatesPipe.putNext((events.updatedIncomingThreadReadStates, events.updatedOutgoingThreadReadStates))
|
||||
}
|
||||
if !events.isContactUpdates.isEmpty {
|
||||
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
||||
}
|
||||
|
@ -1187,7 +1187,14 @@ public final class AccountViewTracker {
|
||||
}
|
||||
})
|
||||
|
||||
if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
let peerId: PeerId
|
||||
switch chatLocation {
|
||||
case let .peer(peerIdValue):
|
||||
peerId = peerIdValue
|
||||
case let .external(peerIdValue, _):
|
||||
peerId = peerIdValue
|
||||
}
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
return Signal { subscriber in
|
||||
let combinedDisposable = MetaDisposable()
|
||||
self.queue.async {
|
||||
@ -1203,7 +1210,7 @@ public final class AccountViewTracker {
|
||||
let _ = self.account?.postbox.transaction({ transaction -> Void in
|
||||
if transaction.getPeerChatListIndex(peerId) == nil {
|
||||
if let message = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) {
|
||||
transaction.addHole(peerId: peerId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: message.id + 1 ... (Int32.max - 1))
|
||||
//transaction.addHole(peerId: peerId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: message.id + 1 ... (Int32.max - 1))
|
||||
}
|
||||
}
|
||||
}).start()
|
||||
|
@ -67,6 +67,16 @@ public func deleteAllMessagesWithAuthor(transaction: Transaction, mediaBox: Medi
|
||||
}
|
||||
}
|
||||
|
||||
public func deleteAllMessagesWithForwardAuthor(transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace) {
|
||||
var resourceIds: [WrappedMediaResourceId] = []
|
||||
transaction.removeAllMessagesWithForwardAuthor(peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, forEachMedia: { media in
|
||||
addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds)
|
||||
})
|
||||
if !resourceIds.isEmpty {
|
||||
let _ = mediaBox.removeCachedResources(Set(resourceIds)).start()
|
||||
}
|
||||
}
|
||||
|
||||
public func clearHistory(transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, namespaces: MessageIdNamespaces) {
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
var resourceIds: [WrappedMediaResourceId] = []
|
||||
|
@ -6,19 +6,29 @@ import MtProtoKit
|
||||
|
||||
import SyncCore
|
||||
|
||||
public struct PeerActivitySpace: Hashable {
|
||||
public var peerId: PeerId
|
||||
public var threadId: Int64?
|
||||
|
||||
public init(peerId: PeerId, threadId: Int64?) {
|
||||
self.peerId = peerId
|
||||
self.threadId = threadId
|
||||
}
|
||||
}
|
||||
|
||||
struct PeerInputActivityRecord: Equatable {
|
||||
let activity: PeerInputActivity
|
||||
let updateId: Int32
|
||||
}
|
||||
|
||||
private final class ManagedLocalTypingActivitiesContext {
|
||||
private var disposables: [PeerId: (PeerInputActivityRecord, MetaDisposable)] = [:]
|
||||
private var disposables: [PeerActivitySpace: (PeerInputActivityRecord, MetaDisposable)] = [:]
|
||||
|
||||
func update(activities: [PeerId: [PeerId: PeerInputActivityRecord]]) -> (start: [(PeerId, PeerInputActivityRecord?, MetaDisposable)], dispose: [MetaDisposable]) {
|
||||
var start: [(PeerId, PeerInputActivityRecord?, MetaDisposable)] = []
|
||||
func update(activities: [PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) -> (start: [(PeerActivitySpace, PeerInputActivityRecord?, MetaDisposable)], dispose: [MetaDisposable]) {
|
||||
var start: [(PeerActivitySpace, PeerInputActivityRecord?, MetaDisposable)] = []
|
||||
var dispose: [MetaDisposable] = []
|
||||
|
||||
var validPeerIds = Set<PeerId>()
|
||||
var validPeerIds = Set<PeerActivitySpace>()
|
||||
for (peerId, record) in activities {
|
||||
if let activity = record.values.first {
|
||||
validPeerIds.insert(peerId)
|
||||
@ -37,7 +47,7 @@ private final class ManagedLocalTypingActivitiesContext {
|
||||
}
|
||||
}
|
||||
|
||||
var removePeerIds: [PeerId] = []
|
||||
var removePeerIds: [PeerActivitySpace] = []
|
||||
for key in self.disposables.keys {
|
||||
if !validPeerIds.contains(key) {
|
||||
removePeerIds.append(key)
|
||||
@ -60,7 +70,7 @@ private final class ManagedLocalTypingActivitiesContext {
|
||||
}
|
||||
}
|
||||
|
||||
func managedLocalTypingActivities(activities: Signal<[PeerId: [PeerId: PeerInputActivityRecord]], NoError>, postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> {
|
||||
func managedLocalTypingActivities(activities: Signal<[PeerActivitySpace: [PeerId: PeerInputActivityRecord]], NoError>, postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> {
|
||||
return Signal { subscriber in
|
||||
let context = Atomic(value: ManagedLocalTypingActivitiesContext())
|
||||
let disposable = activities.start(next: { activities in
|
||||
@ -73,7 +83,7 @@ func managedLocalTypingActivities(activities: Signal<[PeerId: [PeerId: PeerInput
|
||||
}
|
||||
|
||||
for (peerId, activity, disposable) in start {
|
||||
disposable.set(requestActivity(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, activity: activity?.activity).start())
|
||||
disposable.set(requestActivity(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId.peerId, threadId: peerId.threadId, activity: activity?.activity).start())
|
||||
}
|
||||
})
|
||||
return ActionDisposable {
|
||||
@ -111,7 +121,7 @@ private func actionFromActivity(_ activity: PeerInputActivity?) -> Api.SendMessa
|
||||
}
|
||||
}
|
||||
|
||||
private func requestActivity(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, activity: PeerInputActivity?) -> Signal<Void, NoError> {
|
||||
private func requestActivity(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadId: Int64?, activity: PeerInputActivity?) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
if peerId == accountPeerId {
|
||||
@ -122,7 +132,12 @@ private func requestActivity(postbox: Postbox, network: Network, accountPeerId:
|
||||
}
|
||||
|
||||
if let inputPeer = apiInputPeer(peer) {
|
||||
return network.request(Api.functions.messages.setTyping(peer: inputPeer, action: actionFromActivity(activity)))
|
||||
var flags: Int32 = 0
|
||||
let topMessageId = threadId.flatMap { makeThreadIdMessageId(peerId: peerId, threadId: $0) }
|
||||
if topMessageId != nil {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
return network.request(Api.functions.messages.setTyping(flags: flags, peer: inputPeer, topMsgId: topMessageId?.id, action: actionFromActivity(activity)))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|
@ -178,9 +178,9 @@ private final class PeerInputActivityContext {
|
||||
}
|
||||
|
||||
private final class PeerGlobalInputActivityContext {
|
||||
private let subscribers = Bag<([PeerId: [PeerId: PeerInputActivityRecord]]) -> Void>()
|
||||
private let subscribers = Bag<([PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) -> Void>()
|
||||
|
||||
func addSubscriber(_ subscriber: @escaping ([PeerId: [PeerId: PeerInputActivityRecord]]) -> Void) -> Int {
|
||||
func addSubscriber(_ subscriber: @escaping ([PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) -> Void) -> Int {
|
||||
return self.subscribers.add(subscriber)
|
||||
}
|
||||
|
||||
@ -192,7 +192,7 @@ private final class PeerGlobalInputActivityContext {
|
||||
return self.subscribers.isEmpty
|
||||
}
|
||||
|
||||
func notify(_ activities: [PeerId: [PeerId: PeerInputActivityRecord]]) {
|
||||
func notify(_ activities: [PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) {
|
||||
for subscriber in self.subscribers.copyItems() {
|
||||
subscriber(activities)
|
||||
}
|
||||
@ -204,10 +204,10 @@ final class PeerInputActivityManager {
|
||||
|
||||
private var nextEpisodeId: Int32 = 0
|
||||
private var nextUpdateId: Int32 = 0
|
||||
private var contexts: [PeerId: PeerInputActivityContext] = [:]
|
||||
private var contexts: [PeerActivitySpace: PeerInputActivityContext] = [:]
|
||||
private var globalContext: PeerGlobalInputActivityContext?
|
||||
|
||||
func activities(peerId: PeerId) -> Signal<[(PeerId, PeerInputActivityRecord)], NoError> {
|
||||
func activities(peerId: PeerActivitySpace) -> Signal<[(PeerId, PeerInputActivityRecord)], NoError> {
|
||||
let queue = self.queue
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
@ -256,10 +256,10 @@ final class PeerInputActivityManager {
|
||||
}
|
||||
}
|
||||
|
||||
private func collectActivities() -> [PeerId: [PeerId: PeerInputActivityRecord]] {
|
||||
private func collectActivities() -> [PeerActivitySpace: [PeerId: PeerInputActivityRecord]] {
|
||||
assert(self.queue.isCurrent())
|
||||
|
||||
var dict: [PeerId: [PeerId: PeerInputActivityRecord]] = [:]
|
||||
var dict: [PeerActivitySpace: [PeerId: PeerInputActivityRecord]] = [:]
|
||||
for (chatPeerId, context) in self.contexts {
|
||||
var chatDict: [PeerId: PeerInputActivityRecord] = [:]
|
||||
for (peerId, activity) in context.topActivities() {
|
||||
@ -270,7 +270,7 @@ final class PeerInputActivityManager {
|
||||
return dict
|
||||
}
|
||||
|
||||
func allActivities() -> Signal<[PeerId: [PeerId: PeerInputActivityRecord]], NoError> {
|
||||
func allActivities() -> Signal<[PeerActivitySpace: [PeerId: PeerInputActivityRecord]], NoError> {
|
||||
let queue = self.queue
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
@ -306,7 +306,7 @@ final class PeerInputActivityManager {
|
||||
}
|
||||
}
|
||||
|
||||
func addActivity(chatPeerId: PeerId, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
|
||||
func addActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
|
||||
self.queue.async {
|
||||
let context: PeerInputActivityContext
|
||||
if let currentContext = self.contexts[chatPeerId] {
|
||||
@ -338,7 +338,7 @@ final class PeerInputActivityManager {
|
||||
}
|
||||
}
|
||||
|
||||
func removeActivity(chatPeerId: PeerId, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
|
||||
func removeActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
|
||||
self.queue.async {
|
||||
if let context = self.contexts[chatPeerId] {
|
||||
context.removeActivity(peerId: peerId, activity: activity, episodeId: episodeId)
|
||||
@ -351,7 +351,7 @@ final class PeerInputActivityManager {
|
||||
}
|
||||
}
|
||||
|
||||
func removeAllActivities(chatPeerId: PeerId, peerId: PeerId) {
|
||||
func removeAllActivities(chatPeerId: PeerActivitySpace, peerId: PeerId) {
|
||||
self.queue.async {
|
||||
if let currentContext = self.contexts[chatPeerId] {
|
||||
currentContext.removeAllActivities(peerId: peerId)
|
||||
@ -370,7 +370,7 @@ final class PeerInputActivityManager {
|
||||
}
|
||||
}
|
||||
|
||||
func acquireActivity(chatPeerId: PeerId, peerId: PeerId, activity: PeerInputActivity) -> Disposable {
|
||||
func acquireActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
let queue = self.queue
|
||||
queue.async {
|
||||
|
@ -41,6 +41,7 @@ private final class PendingMessageContext {
|
||||
var state: PendingMessageState = .none
|
||||
let uploadDisposable = MetaDisposable()
|
||||
let sendDisposable = MetaDisposable()
|
||||
var threadId: Int64?
|
||||
var activityType: PeerInputActivity? = nil
|
||||
var contentType: PendingMessageUploadedContentType? = nil
|
||||
let activityDisposable = MetaDisposable()
|
||||
@ -371,6 +372,7 @@ public final class PendingMessageManager {
|
||||
}
|
||||
|
||||
messageContext.activityType = uploadActivityTypeForMessage(message)
|
||||
messageContext.threadId = message.threadId
|
||||
strongSelf.collectUploadingInfo(messageContext: messageContext, message: message)
|
||||
}
|
||||
|
||||
@ -425,7 +427,7 @@ public final class PendingMessageManager {
|
||||
|
||||
for (messageContext, message, type, contentUploadSignal) in messagesToUpload {
|
||||
if strongSelf.canBeginUploadingMessage(id: message.id, type: type) {
|
||||
strongSelf.beginUploadingMessage(messageContext: messageContext, id: message.id, groupId: message.groupingKey, uploadSignal: contentUploadSignal)
|
||||
strongSelf.beginUploadingMessage(messageContext: messageContext, id: message.id, threadId: message.threadId, groupId: message.groupingKey, uploadSignal: contentUploadSignal)
|
||||
} else {
|
||||
messageContext.state = .waitingForUploadToStart(groupId: message.groupingKey, upload: contentUploadSignal)
|
||||
}
|
||||
@ -544,7 +546,7 @@ public final class PendingMessageManager {
|
||||
messageContext.state = .collectingInfo(message: message)
|
||||
}
|
||||
|
||||
private func beginUploadingMessage(messageContext: PendingMessageContext, id: MessageId, groupId: Int64?, uploadSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>) {
|
||||
private func beginUploadingMessage(messageContext: PendingMessageContext, id: MessageId, threadId: Int64?, groupId: Int64?, uploadSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>) {
|
||||
messageContext.state = .uploading(groupId: groupId)
|
||||
|
||||
let status = PendingMessageStatus(isRunning: true, progress: 0.0)
|
||||
@ -552,7 +554,7 @@ public final class PendingMessageManager {
|
||||
for subscriber in messageContext.statusSubscribers.copyItems() {
|
||||
subscriber(messageContext.status, messageContext.error)
|
||||
}
|
||||
self.addContextActivityIfNeeded(messageContext, peerId: id.peerId)
|
||||
self.addContextActivityIfNeeded(messageContext, peerId: PeerActivitySpace(peerId: id.peerId, threadId: threadId))
|
||||
|
||||
let queue = self.queue
|
||||
|
||||
@ -602,7 +604,7 @@ public final class PendingMessageManager {
|
||||
}))
|
||||
}
|
||||
|
||||
private func addContextActivityIfNeeded(_ context: PendingMessageContext, peerId: PeerId) {
|
||||
private func addContextActivityIfNeeded(_ context: PendingMessageContext, peerId: PeerActivitySpace) {
|
||||
if let activityType = context.activityType {
|
||||
context.activityDisposable.set(self.localInputActivityManager.acquireActivity(chatPeerId: peerId, peerId: self.accountPeerId, activity: activityType))
|
||||
}
|
||||
@ -622,7 +624,7 @@ public final class PendingMessageManager {
|
||||
for subscriber in context.statusSubscribers.copyItems() {
|
||||
subscriber(context.status, context.error)
|
||||
}
|
||||
self.addContextActivityIfNeeded(context, peerId: peerId)
|
||||
self.addContextActivityIfNeeded(context, peerId: PeerActivitySpace(peerId: peerId, threadId: context.threadId))
|
||||
context.uploadDisposable.set((uploadSignal
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] next in
|
||||
if let strongSelf = self {
|
||||
|
@ -14,7 +14,8 @@ private class ReplyThreadHistoryContextImpl {
|
||||
struct State: Equatable {
|
||||
var messageId: MessageId
|
||||
var holeIndices: [MessageId.Namespace: IndexSet]
|
||||
var maxReadMessageId: MessageId?
|
||||
var maxReadIncomingMessageId: MessageId?
|
||||
var maxReadOutgoingMessageId: MessageId?
|
||||
}
|
||||
|
||||
let state = Promise<State>()
|
||||
@ -28,15 +29,28 @@ private class ReplyThreadHistoryContextImpl {
|
||||
}
|
||||
}
|
||||
|
||||
let maxReadOutgoingMessageId = Promise<MessageId?>()
|
||||
private var maxReadOutgoingMessageIdValue: MessageId? {
|
||||
didSet {
|
||||
if self.maxReadOutgoingMessageIdValue != oldValue {
|
||||
self.maxReadOutgoingMessageId.set(.single(nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var initialStateDisposable: Disposable?
|
||||
private var holesDisposable: Disposable?
|
||||
private var readStateDisposable: Disposable?
|
||||
private let readDisposable = MetaDisposable()
|
||||
|
||||
init(queue: Queue, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadMessageId: MessageId?) {
|
||||
init(queue: Queue, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
|
||||
self.queue = queue
|
||||
self.account = account
|
||||
self.messageId = messageId
|
||||
|
||||
self.maxReadOutgoingMessageIdValue = 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 {
|
||||
@ -57,7 +71,7 @@ private class ReplyThreadHistoryContextImpl {
|
||||
indices = IndexSet()
|
||||
}*/
|
||||
}
|
||||
return State(messageId: messageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadMessageId: maxReadMessageId)
|
||||
return State(messageId: messageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] state in
|
||||
guard let strongSelf = self else {
|
||||
@ -88,6 +102,16 @@ private class ReplyThreadHistoryContextImpl {
|
||||
}
|
||||
strongSelf.setCurrentHole(entry: entry)
|
||||
})
|
||||
|
||||
self.readStateDisposable = (account.stateManager.threadReadStateUpdates
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] (_, outgoing) in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let value = outgoing[messageId] {
|
||||
strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud, id: value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -207,7 +231,8 @@ public class ReplyThreadHistoryContext {
|
||||
subscriber.putNext(MessageHistoryViewExternalInput(
|
||||
peerId: state.messageId.peerId,
|
||||
threadId: makeMessageThreadId(state.messageId),
|
||||
maxReadMessageId: state.maxReadMessageId,
|
||||
maxReadIncomingMessageId: state.maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: state.maxReadOutgoingMessageId,
|
||||
holes: state.holeIndices
|
||||
))
|
||||
})
|
||||
@ -218,10 +243,24 @@ public class ReplyThreadHistoryContext {
|
||||
}
|
||||
}
|
||||
|
||||
public init(account: Account, peerId: PeerId, threadMessageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadMessageId: MessageId?) {
|
||||
public var maxReadOutgoingMessageId: Signal<MessageId?, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.maxReadOutgoingMessageId.get().start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}))
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public init(account: Account, peerId: PeerId, threadMessageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return ReplyThreadHistoryContextImpl(queue: queue, account: account, messageId: threadMessageId, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId)
|
||||
return ReplyThreadHistoryContextImpl(queue: queue, account: account, messageId: threadMessageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
})
|
||||
}
|
||||
|
||||
@ -240,12 +279,14 @@ public struct ChatReplyThreadMessage {
|
||||
|
||||
public var messageId: MessageId
|
||||
public var maxMessage: MaxMessage
|
||||
public var maxReadMessageId: MessageId?
|
||||
public var maxReadIncomingMessageId: MessageId?
|
||||
public var maxReadOutgoingMessageId: MessageId?
|
||||
|
||||
public init(messageId: MessageId, maxMessage: MaxMessage, maxReadMessageId: MessageId?) {
|
||||
public init(messageId: MessageId, maxMessage: MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
|
||||
self.messageId = messageId
|
||||
self.maxMessage = maxMessage
|
||||
self.maxReadMessageId = maxReadMessageId
|
||||
self.maxReadIncomingMessageId = maxReadIncomingMessageId
|
||||
self.maxReadOutgoingMessageId = maxReadOutgoingMessageId
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,42 +304,14 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
let maxMessage: Signal<Int32?, NoError> = account.network.request(Api.functions.messages.getMessagesViews(peer: inputPeer, id: [messageId.id], increment: .boolFalse))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.MessageViews?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> Int32? in
|
||||
guard let result = result else {
|
||||
return nil
|
||||
}
|
||||
var maxId: Int32?
|
||||
switch result {
|
||||
case let .messageViews(views, _, _):
|
||||
for view in views {
|
||||
switch view {
|
||||
case let .messageViews(_, _, _, replies):
|
||||
if let replies = replies {
|
||||
switch replies {
|
||||
case let .messageReplies(_, _, _, _, _, maxIdValue, readMaxIdValue):
|
||||
maxId = maxIdValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxId
|
||||
}
|
||||
|
||||
return combineLatest(discussionMessage, maxMessage)
|
||||
|> mapToSignal { discussionMessage, maxMessage -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
return discussionMessage
|
||||
|> mapToSignal { discussionMessage -> Signal<ChatReplyThreadMessage?, NoError> in
|
||||
guard let discussionMessage = discussionMessage else {
|
||||
return .single(nil)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> ChatReplyThreadMessage? in
|
||||
switch discussionMessage {
|
||||
//messages.discussionMessage flags:# messages:Vector<Message> max_id:flags.0?int read_max_id:flags.1?int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
|
||||
case let .discussionMessage(_, messages, maxId, readMaxId, chats, users):
|
||||
case let .discussionMessage(_, messages, maxId, readInboxMaxId, readOutboxMaxId, chats, users):
|
||||
let parsedMessages = messages.compactMap { message -> StoreMessage? in
|
||||
StoreMessage(apiMessage: message)
|
||||
}
|
||||
@ -332,11 +345,11 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
let resolvedMaxMessage: ChatReplyThreadMessage.MaxMessage
|
||||
if let maxMessage = maxMessage {
|
||||
if let maxId = maxId {
|
||||
resolvedMaxMessage = .known(MessageId(
|
||||
peerId: parsedIndex.id.peerId,
|
||||
namespace: Namespaces.Message.Cloud,
|
||||
id: maxMessage
|
||||
id: maxId
|
||||
))
|
||||
} else {
|
||||
resolvedMaxMessage = .known(nil)
|
||||
@ -345,7 +358,10 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
return ChatReplyThreadMessage(
|
||||
messageId: parsedIndex.id,
|
||||
maxMessage: resolvedMaxMessage,
|
||||
maxReadMessageId: readMaxId.flatMap { readMaxId in
|
||||
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
|
||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||
},
|
||||
maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in
|
||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||
}
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -303,29 +303,39 @@ public final class AccountContextImpl: AccountContext {
|
||||
switch location {
|
||||
case let .peer(peerId):
|
||||
return .peer(peerId)
|
||||
case let .replyThread(messageId, _, maxMessage, maxReadMessageId):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
public func chatLocationOutgoingReadState(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<MessageId?, NoError> {
|
||||
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)
|
||||
return context.maxReadOutgoingMessageId
|
||||
}
|
||||
}
|
||||
|
||||
public func applyMaxReadIndex(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>, messageIndex: MessageIndex) {
|
||||
switch location {
|
||||
case .peer:
|
||||
let _ = applyMaxReadIndexInteractively(postbox: self.account.postbox, stateManager: self.account.stateManager, index: messageIndex).start()
|
||||
case let .replyThread(messageId, _, maxMessage, maxReadMessageId):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId)
|
||||
case let .replyThread(messageId, _, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
context.applyMaxReadIndex(messageIndex: messageIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadMessageId: MessageId?) -> ReplyThreadHistoryContext {
|
||||
private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) -> ReplyThreadHistoryContext {
|
||||
let holder = holder.modify { current in
|
||||
if let current = current as? ChatLocationContextHolderImpl {
|
||||
return current
|
||||
} else {
|
||||
return ChatLocationContextHolderImpl(account: account, messageId: messageId, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId)
|
||||
return ChatLocationContextHolderImpl(account: account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
|
||||
}
|
||||
} as! ChatLocationContextHolderImpl
|
||||
return holder.context
|
||||
@ -334,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, maxReadMessageId: MessageId?) {
|
||||
self.context = ReplyThreadHistoryContext(account: account, peerId: messageId.peerId, threadMessageId: messageId, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ extension ChatLocation {
|
||||
switch self {
|
||||
case let .peer(peerId):
|
||||
return peerId
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
return messageId.peerId
|
||||
}
|
||||
}
|
||||
@ -371,7 +371,7 @@ 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(messageId, _, _, _, _):
|
||||
locationBroadcastPanelSource = .none
|
||||
let promise = Promise<Message?>()
|
||||
let key = PostboxViewKey.messages([messageId])
|
||||
@ -480,7 +480,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: strongSelf.chatLocation, chatLocationContextHolder: strongSelf.chatLocationContextHolder, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: {
|
||||
var openChatLocation = strongSelf.chatLocation
|
||||
if case let .replyThread(messageId, _, _, _, _) = openChatLocation {
|
||||
if message.threadId != makeMessageThreadId(messageId) {
|
||||
openChatLocation = .peer(message.id.peerId)
|
||||
}
|
||||
}
|
||||
|
||||
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: openChatLocation, chatLocationContextHolder: strongSelf.chatLocationContextHolder, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: {
|
||||
self?.chatDisplayNode.dismissInput()
|
||||
}, present: { c, a in
|
||||
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
@ -1594,6 +1601,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
if let strongSelf = self {
|
||||
if case .replyThread(message.id, _, _, _, _) = strongSelf.chatLocation {
|
||||
return .none
|
||||
}
|
||||
|
||||
if canReplyInChat(strongSelf.presentationInterfaceState) {
|
||||
return .reply
|
||||
} else if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||
@ -1629,7 +1640,7 @@ 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, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
let peerId = 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)
|
||||
}
|
||||
@ -2180,36 +2191,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
let foundIndex = Promise<ReplyThreadInfo?>()
|
||||
foundIndex.set(fetchAndPreloadReplyThreadInfo(context: strongSelf.context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId)))
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
strongSelf.present(statusController, in: .window(.root))
|
||||
|
||||
let disposable = (foundIndex.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak statusController] result in
|
||||
statusController?.dismiss()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
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)
|
||||
}
|
||||
}, openReplyThreadOriginalMessage: { [weak self] message in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var threadMessageId: MessageId?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyMessageAttribute {
|
||||
threadMessageId = attribute.threadMessageId
|
||||
break
|
||||
}
|
||||
|
||||
if let result = result {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
let chatLocation: ChatLocation = .replyThread(threadMessageId: result.message.messageId, isChannelPost: result.isChannelPost, maxMessage: result.message.maxMessage, maxReadMessageId: result.message.maxReadMessageId)
|
||||
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, activateInput: result.isEmpty, keepStack: .always))
|
||||
}
|
||||
for attribute in message.attributes {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
strongSelf.navigateToMessage(from: nil, to: .id(attribute.messageId))
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
cancelImpl = { [weak statusController] in
|
||||
disposable.dispose()
|
||||
statusController?.dismiss()
|
||||
}
|
||||
}, requestMessageUpdate: { [weak self] id in
|
||||
if let strongSelf = self {
|
||||
@ -2641,7 +2651,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch chatLocation {
|
||||
case .peer:
|
||||
replyThreadType = .replies
|
||||
case let .replyThread(_, isChannelPost, _, _):
|
||||
case let .replyThread(_, isChannelPost, _, _, _):
|
||||
isReplyThread = true
|
||||
if isChannelPost {
|
||||
replyThreadType = .comments
|
||||
@ -2983,22 +2993,30 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.startBot(botStart.payload)
|
||||
}
|
||||
|
||||
let activitySpace: PeerActivitySpace
|
||||
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))
|
||||
}
|
||||
|
||||
self.inputActivityDisposable = (self.typingActivityPromise.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
if let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation {
|
||||
strongSelf.context.account.updateLocalInputActivity(peerId: peerId, activity: .typingText, isPresent: value)
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: value)
|
||||
}
|
||||
})
|
||||
|
||||
self.recordingActivityDisposable = (self.recordingActivityPromise.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
if let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation {
|
||||
if let strongSelf = self {
|
||||
strongSelf.acquiredRecordingActivityDisposable?.dispose()
|
||||
switch value {
|
||||
case .voice:
|
||||
strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: peerId, activity: .recordingVoice)
|
||||
strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: activitySpace, activity: .recordingVoice)
|
||||
case .instantVideo:
|
||||
strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: peerId, activity: .recordingInstantVideo)
|
||||
strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: activitySpace, activity: .recordingInstantVideo)
|
||||
case .none:
|
||||
strongSelf.acquiredRecordingActivityDisposable = nil
|
||||
}
|
||||
@ -3398,7 +3416,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else if let _ = cachedData as? CachedSecretChatData {
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, _, _, _) = strongSelf.chatLocation {
|
||||
if case let .replyThread(messageId, _, _, _, _) = strongSelf.chatLocation {
|
||||
if isTopReplyThreadMessageShown {
|
||||
pinnedMessageId = nil
|
||||
} else {
|
||||
@ -3905,6 +3923,58 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self, !messages.isEmpty {
|
||||
presentPeerReportOptions(context: strongSelf.context, parent: strongSelf, contextController: contextController, subject: .messages(messages.map({ $0.id }).sorted()), completion: { _ in })
|
||||
}
|
||||
}, blockMessageAuthor: { [weak self] message, contextController in
|
||||
contextController?.dismiss(completion: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let author = message.forwardInfo?.author
|
||||
|
||||
guard let peer = author else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = strongSelf.presentationData
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
var reportSpam = true
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetTextItem(title: presentationData.strings.UserInfo_BlockConfirmationTitle(peer.compactDisplayTitle).0))
|
||||
items.append(contentsOf: [
|
||||
ActionSheetCheckboxItem(title: presentationData.strings.Conversation_Moderate_Report, label: "", value: reportSpam, action: { [weak controller] checkValue in
|
||||
reportSpam = checkValue
|
||||
controller?.updateItem(groupIndex: 0, itemIndex: 1, { item in
|
||||
if let item = item as? ActionSheetCheckboxItem {
|
||||
return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action)
|
||||
}
|
||||
return item
|
||||
})
|
||||
}),
|
||||
ActionSheetButtonItem(title: presentationData.strings.Replies_BlockAndDeleteRepliesActionTitle, color: .destructive, action: {
|
||||
dismissAction()
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = requestUpdatePeerIsBlocked(account: strongSelf.context.account, peerId: peer.id, isBlocked: true).start()
|
||||
let account = strongSelf.context.account
|
||||
let _ = (strongSelf.context.account.postbox.transaction { transasction -> Void in
|
||||
deleteAllMessagesWithForwardAuthor(transaction: transasction, mediaBox: account.postbox.mediaBox, peerId: message.id.peerId, forwardAuthorId: peer.id, namespace: Namespaces.Message.Cloud)
|
||||
}).start()
|
||||
if reportSpam {
|
||||
let _ = TelegramCore.reportPeer(account: strongSelf.context.account, peerId: peer.id, reason: .spam).start()
|
||||
}
|
||||
})
|
||||
] as [ActionSheetItem])
|
||||
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: items),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
})
|
||||
}, deleteMessages: { [weak self] messages, contextController, completion in
|
||||
if let strongSelf = self, !messages.isEmpty {
|
||||
let messageIds = Set(messages.map { $0.id })
|
||||
@ -5035,68 +5105,73 @@ 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, maxReadMessageId: replyThreadResult.maxReadMessageId), subject: subject, keepStack: .always))
|
||||
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))
|
||||
}
|
||||
}, 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()))
|
||||
|
||||
do {
|
||||
let peerId = self.chatLocation.peerId
|
||||
if let subject = self.subject, case .scheduledMessages = subject {
|
||||
} else if case .replyThread = self.chatLocation {
|
||||
} else {
|
||||
let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.peer(peerId), .total(nil)])
|
||||
let notificationSettingsKey: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId]))
|
||||
self.chatUnreadCountDisposable = (self.context.account.postbox.combinedView(keys: [unreadCountsKey, notificationSettingsKey])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] views in
|
||||
if let strongSelf = self {
|
||||
var unreadCount: Int32 = 0
|
||||
var totalChatCount: Int32 = 0
|
||||
|
||||
let inAppSettings = strongSelf.context.sharedContext.currentInAppNotificationSettings.with { $0 }
|
||||
if let view = views.views[unreadCountsKey] as? UnreadMessageCountsView {
|
||||
if let count = view.count(for: .peer(peerId)) {
|
||||
unreadCount = count
|
||||
}
|
||||
if let (_, state) = view.total() {
|
||||
let (count, _) = renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: state)
|
||||
totalChatCount = count
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.navigateButtons.unreadCount = unreadCount
|
||||
|
||||
if let view = views.views[notificationSettingsKey] as? PeerNotificationSettingsView, let notificationSettings = view.notificationSettings[peerId] {
|
||||
var globalRemainingUnreadChatCount = totalChatCount
|
||||
if !notificationSettings.isRemovedFromTotalUnreadCount(default: false) && unreadCount > 0 {
|
||||
if case .messages = inAppSettings.totalUnreadCountDisplayCategory {
|
||||
globalRemainingUnreadChatCount -= unreadCount
|
||||
} else {
|
||||
globalRemainingUnreadChatCount -= 1
|
||||
if case .peer = self.chatLocation {
|
||||
let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.peer(peerId), .total(nil)])
|
||||
let notificationSettingsKey: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId]))
|
||||
self.chatUnreadCountDisposable = (self.context.account.postbox.combinedView(keys: [unreadCountsKey, notificationSettingsKey])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] views in
|
||||
if let strongSelf = self {
|
||||
var unreadCount: Int32 = 0
|
||||
var totalChatCount: Int32 = 0
|
||||
|
||||
let inAppSettings = strongSelf.context.sharedContext.currentInAppNotificationSettings.with { $0 }
|
||||
if let view = views.views[unreadCountsKey] as? UnreadMessageCountsView {
|
||||
if let count = view.count(for: .peer(peerId)) {
|
||||
unreadCount = count
|
||||
}
|
||||
if let (_, state) = view.total() {
|
||||
let (count, _) = renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: state)
|
||||
totalChatCount = count
|
||||
}
|
||||
}
|
||||
|
||||
if globalRemainingUnreadChatCount > 0 {
|
||||
strongSelf.navigationItem.badge = "\(globalRemainingUnreadChatCount)"
|
||||
} else {
|
||||
strongSelf.navigationItem.badge = ""
|
||||
strongSelf.chatDisplayNode.navigateButtons.unreadCount = unreadCount
|
||||
|
||||
if let view = views.views[notificationSettingsKey] as? PeerNotificationSettingsView, let notificationSettings = view.notificationSettings[peerId] {
|
||||
var globalRemainingUnreadChatCount = totalChatCount
|
||||
if !notificationSettings.isRemovedFromTotalUnreadCount(default: false) && unreadCount > 0 {
|
||||
if case .messages = inAppSettings.totalUnreadCountDisplayCategory {
|
||||
globalRemainingUnreadChatCount -= unreadCount
|
||||
} else {
|
||||
globalRemainingUnreadChatCount -= 1
|
||||
}
|
||||
}
|
||||
|
||||
if globalRemainingUnreadChatCount > 0 {
|
||||
strongSelf.navigationItem.badge = "\(globalRemainingUnreadChatCount)"
|
||||
} else {
|
||||
strongSelf.navigationItem.badge = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesCount(peerId: peerId) |> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
if let strongSelf = self {
|
||||
if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0
|
||||
} else {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = count
|
||||
})
|
||||
|
||||
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesCount(peerId: peerId) |> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
if let strongSelf = self {
|
||||
if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0
|
||||
} else {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = count
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let postbox = self.context.account.postbox
|
||||
let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:])
|
||||
self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: peerId)
|
||||
var activityThreadId: Int64?
|
||||
if case let .replyThread(messageId, _, _, _, _) = self.chatLocation {
|
||||
activityThreadId = makeMessageThreadId(messageId)
|
||||
}
|
||||
self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: PeerActivitySpace(peerId: peerId, threadId: activityThreadId))
|
||||
|> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in
|
||||
var foundAllPeers = true
|
||||
var cachedResult: [(Peer, PeerInputActivity)] = []
|
||||
@ -7491,7 +7566,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch self.chatLocation {
|
||||
case .peer:
|
||||
break
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
defaultReplyMessageId = messageId
|
||||
}
|
||||
|
||||
@ -7538,7 +7613,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch self.chatLocation {
|
||||
case let .peer(peerIdValue):
|
||||
peerId = peerIdValue
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
peerId = messageId.peerId
|
||||
}
|
||||
|
||||
@ -7960,7 +8035,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch self.chatLocation {
|
||||
case .peer:
|
||||
break
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
searchTopMsgId = messageId
|
||||
}
|
||||
switch search.domain {
|
||||
@ -8176,6 +8251,43 @@ 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)))
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let statusController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
present(statusController, nil)
|
||||
|
||||
let disposable = (foundIndex.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak statusController] result in
|
||||
statusController?.dismiss()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, subject: subject, activateInput: result.isEmpty, keepStack: .always))
|
||||
}
|
||||
})
|
||||
|
||||
cancelImpl = { [weak statusController] in
|
||||
disposable.dispose()
|
||||
statusController?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, dropStack: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) {
|
||||
let scrollPosition: ListViewScrollPosition
|
||||
if case .upperBound = messageLocation {
|
||||
|
@ -113,6 +113,7 @@ public final class ChatControllerInteraction {
|
||||
let greetingStickerNode: () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?
|
||||
let openPeerContextMenu: (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void
|
||||
let openMessageReplies: (MessageId, Bool) -> Void
|
||||
let openReplyThreadOriginalMessage: (Message) -> Void
|
||||
|
||||
let requestMessageUpdate: (MessageId) -> Void
|
||||
let cancelInteractiveKeyboardGestures: () -> Void
|
||||
@ -196,6 +197,7 @@ public final class ChatControllerInteraction {
|
||||
greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?,
|
||||
openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void,
|
||||
openMessageReplies: @escaping (MessageId, Bool) -> Void,
|
||||
openReplyThreadOriginalMessage: @escaping (Message) -> Void,
|
||||
requestMessageUpdate: @escaping (MessageId) -> Void,
|
||||
cancelInteractiveKeyboardGestures: @escaping () -> Void,
|
||||
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
||||
@ -267,6 +269,7 @@ public final class ChatControllerInteraction {
|
||||
self.greetingStickerNode = greetingStickerNode
|
||||
self.openPeerContextMenu = openPeerContextMenu
|
||||
self.openMessageReplies = openMessageReplies
|
||||
self.openReplyThreadOriginalMessage = openReplyThreadOriginalMessage
|
||||
|
||||
self.requestMessageUpdate = requestMessageUpdate
|
||||
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
|
||||
@ -316,6 +319,7 @@ public final class ChatControllerInteraction {
|
||||
return nil
|
||||
}, openPeerContextMenu: { _, _, _, _ in
|
||||
}, openMessageReplies: { _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -8,7 +8,7 @@ import AccountContext
|
||||
import TelegramPresentationData
|
||||
|
||||
|
||||
func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, includeUnreadEntry: Bool, includeEmptyEntry: Bool, includeChatInfoEntry: Bool, includeSearchEntry: Bool, reverse: Bool, groupMessages: Bool, selectedMessages: Set<MessageId>?, presentationData: ChatPresentationData, historyAppearsCleared: Bool, associatedData: ChatMessageItemAssociatedData, updatingMedia: [MessageId: ChatUpdatingMessageMedia]) -> [ChatHistoryEntry] {
|
||||
func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, includeUnreadEntry: Bool, includeEmptyEntry: Bool, includeChatInfoEntry: Bool, includeSearchEntry: Bool, reverse: Bool, groupMessages: Bool, selectedMessages: Set<MessageId>?, presentationData: ChatPresentationData, historyAppearsCleared: Bool, associatedData: ChatMessageItemAssociatedData, updatingMedia: [MessageId: ChatUpdatingMessageMedia], customChannelDiscussionReadState: MessageId?, customThreadOutgoingReadState: MessageId?) -> [ChatHistoryEntry] {
|
||||
if historyAppearsCleared {
|
||||
return []
|
||||
}
|
||||
@ -31,9 +31,31 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
|
||||
var groupBucket: [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)] = []
|
||||
loop: for entry in view.entries {
|
||||
var message = entry.message
|
||||
var isRead = entry.isRead
|
||||
|
||||
if let customThreadOutgoingReadState = customThreadOutgoingReadState {
|
||||
isRead = customThreadOutgoingReadState >= message.id
|
||||
}
|
||||
|
||||
if let customChannelDiscussionReadState = customChannelDiscussionReadState {
|
||||
attibuteLoop: for i in 0 ..< message.attributes.count {
|
||||
if let attribute = message.attributes[i] as? ReplyThreadMessageAttribute {
|
||||
if let maxReadMessageId = attribute.maxReadMessageId {
|
||||
if maxReadMessageId < customChannelDiscussionReadState.id {
|
||||
var attributes = message.attributes
|
||||
attributes[i] = ReplyThreadMessageAttribute(count: attribute.count, latestUsers: attribute.latestUsers, commentsPeerId: attribute.commentsPeerId, maxMessageId: attribute.maxMessageId, maxReadMessageId: customChannelDiscussionReadState.id)
|
||||
message = message.withUpdatedAttributes(attributes)
|
||||
}
|
||||
}
|
||||
break attibuteLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var contentTypeHint: ChatMessageEntryContentType = .generic
|
||||
|
||||
for media in entry.message.media {
|
||||
for media in message.media {
|
||||
if media is TelegramMediaDice {
|
||||
contentTypeHint = .animatedEmoji
|
||||
}
|
||||
@ -48,49 +70,49 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
}
|
||||
|
||||
var adminRank: CachedChannelAdminRank?
|
||||
if let author = entry.message.author {
|
||||
if let author = message.author {
|
||||
adminRank = adminRanks[author.id]
|
||||
}
|
||||
|
||||
|
||||
if presentationData.largeEmoji, entry.message.media.isEmpty {
|
||||
if stickersEnabled && entry.message.text.count == 1, let _ = associatedData.animatedEmojiStickers[entry.message.text.basicEmoji.0] {
|
||||
if presentationData.largeEmoji, message.media.isEmpty {
|
||||
if stickersEnabled && message.text.count == 1, let _ = associatedData.animatedEmojiStickers[message.text.basicEmoji.0] {
|
||||
contentTypeHint = .animatedEmoji
|
||||
} else if entry.message.text.count < 10 && messageIsElligibleForLargeEmoji(entry.message) {
|
||||
} else if message.text.count < 10 && messageIsElligibleForLargeEmoji(message) {
|
||||
contentTypeHint = .largeEmoji
|
||||
}
|
||||
}
|
||||
|
||||
if groupMessages {
|
||||
if !groupBucket.isEmpty && entry.message.groupInfo != groupBucket[0].0.groupInfo {
|
||||
if !groupBucket.isEmpty && message.groupInfo != groupBucket[0].0.groupInfo {
|
||||
entries.append(.MessageGroupEntry(groupBucket[0].0.groupInfo!, groupBucket, presentationData))
|
||||
groupBucket.removeAll()
|
||||
}
|
||||
if let _ = entry.message.groupInfo {
|
||||
if let _ = message.groupInfo {
|
||||
let selection: ChatHistoryMessageSelection
|
||||
if let selectedMessages = selectedMessages {
|
||||
selection = .selectable(selected: selectedMessages.contains(entry.message.id))
|
||||
selection = .selectable(selected: selectedMessages.contains(message.id))
|
||||
} else {
|
||||
selection = .none
|
||||
}
|
||||
groupBucket.append((entry.message, entry.isRead, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[entry.message.id])))
|
||||
groupBucket.append((message, entry.isRead, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])))
|
||||
} else {
|
||||
let selection: ChatHistoryMessageSelection
|
||||
if let selectedMessages = selectedMessages {
|
||||
selection = .selectable(selected: selectedMessages.contains(entry.message.id))
|
||||
selection = .selectable(selected: selectedMessages.contains(message.id))
|
||||
} else {
|
||||
selection = .none
|
||||
}
|
||||
entries.append(.MessageEntry(entry.message, presentationData, entry.isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[entry.message.id])))
|
||||
entries.append(.MessageEntry(message, presentationData, entry.isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])))
|
||||
}
|
||||
} else {
|
||||
let selection: ChatHistoryMessageSelection
|
||||
if let selectedMessages = selectedMessages {
|
||||
selection = .selectable(selected: selectedMessages.contains(entry.message.id))
|
||||
selection = .selectable(selected: selectedMessages.contains(message.id))
|
||||
} else {
|
||||
selection = .none
|
||||
}
|
||||
entries.append(.MessageEntry(entry.message, presentationData, entry.isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[entry.message.id])))
|
||||
entries.append(.MessageEntry(message, presentationData, entry.isRead, entry.monthLocation, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])))
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +136,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
}
|
||||
|
||||
var addedThreadHead = false
|
||||
if case let .replyThread(messageId, isChannelPost, _, _) = location, view.earlierId == nil, !view.isLoading {
|
||||
if case let .replyThread(messageId, isChannelPost, _, _, _) = location, view.earlierId == nil, !view.isLoading {
|
||||
loop: for entry in view.additionalData {
|
||||
switch entry {
|
||||
case let .message(id, messages) where id == messageId:
|
||||
|
@ -627,7 +627,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if !isAuxiliaryChat {
|
||||
additionalData.append(.totalUnreadState)
|
||||
}
|
||||
if case let .replyThread(messageId, _, _, _) = chatLocation {
|
||||
if case let .replyThread(messageId, _, _, _, _) = chatLocation {
|
||||
additionalData.append(.cachedPeerData(messageId.peerId))
|
||||
additionalData.append(.peerNotificationSettings(messageId.peerId))
|
||||
if messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
@ -714,6 +714,65 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let customChannelDiscussionReadState: Signal<MessageId?, NoError>
|
||||
if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
let cachedDataKey = PostboxViewKey.cachedPeerData(peerId: chatLocation.peerId)
|
||||
let peerKey = PostboxViewKey.basicPeer(peerId)
|
||||
customChannelDiscussionReadState = context.account.postbox.combinedView(keys: [cachedDataKey, peerKey])
|
||||
|> mapToSignal { views -> Signal<PeerId?, NoError> in
|
||||
guard let view = views.views[cachedDataKey] as? CachedPeerDataView else {
|
||||
return .single(nil)
|
||||
}
|
||||
guard let peer = (views.views[peerKey] as? BasicPeerView)?.peer as? TelegramChannel, case .broadcast = peer.info else {
|
||||
return .single(nil)
|
||||
}
|
||||
guard let cachedData = view.cachedPeerData as? CachedChannelData else {
|
||||
return .single(nil)
|
||||
}
|
||||
guard case let .known(value) = cachedData.linkedDiscussionPeerId else {
|
||||
return .single(nil)
|
||||
}
|
||||
return .single(value)
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { discussionPeerId -> Signal<MessageId?, NoError> in
|
||||
guard let discussionPeerId = discussionPeerId else {
|
||||
return .single(nil)
|
||||
}
|
||||
let key = PostboxViewKey.combinedReadState(peerId: discussionPeerId)
|
||||
return context.account.postbox.combinedView(keys: [key])
|
||||
|> map { views -> MessageId? in
|
||||
guard let view = views.views[key] as? CombinedReadStateView else {
|
||||
return nil
|
||||
}
|
||||
guard let state = view.state else {
|
||||
return nil
|
||||
}
|
||||
for (namespace, namespaceState) in state.states {
|
||||
if namespace == Namespaces.Message.Cloud {
|
||||
switch namespaceState {
|
||||
case let .idBased(maxIncomingReadId, _, _, _, _):
|
||||
return MessageId(peerId: discussionPeerId, namespace: Namespaces.Message.Cloud, id: maxIncomingReadId)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
} else {
|
||||
customChannelDiscussionReadState = .single(nil)
|
||||
}
|
||||
|
||||
let customThreadOutgoingReadState: Signal<MessageId?, NoError>
|
||||
if case .replyThread = chatLocation {
|
||||
customThreadOutgoingReadState = context.chatLocationOutgoingReadState(for: chatLocation, contextHolder: chatLocationContextHolder)
|
||||
} else {
|
||||
customThreadOutgoingReadState = .single(nil)
|
||||
}
|
||||
|
||||
let historyViewTransitionDisposable = combineLatest(queue: messageViewQueue,
|
||||
historyViewUpdate,
|
||||
self.chatPresentationDataPromise.get(),
|
||||
@ -721,8 +780,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
updatingMedia,
|
||||
automaticDownloadNetworkType,
|
||||
self.historyAppearsClearedPromise.get(),
|
||||
animatedEmojiStickers
|
||||
).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, animatedEmojiStickers in
|
||||
animatedEmojiStickers,
|
||||
customChannelDiscussionReadState,
|
||||
customThreadOutgoingReadState
|
||||
).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, animatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState in
|
||||
func applyHole() {
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
@ -806,7 +867,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, isScheduledMessages: isScheduledMessages)
|
||||
|
||||
let filteredEntries = chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, associatedData: associatedData, updatingMedia: updatingMedia)
|
||||
let filteredEntries = chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, associatedData: associatedData, updatingMedia: updatingMedia, customChannelDiscussionReadState: customChannelDiscussionReadState, customThreadOutgoingReadState: customThreadOutgoingReadState)
|
||||
let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0
|
||||
let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id)
|
||||
let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages))
|
||||
@ -1112,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 .replyThread(message.id, _, _, _, _) = self.chatLocation {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
case let .MessageGroupEntry(_, messages, _):
|
||||
@ -1142,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 .replyThread(message.id, _, _, _, _) = self.chatLocation {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
}
|
||||
|
@ -308,11 +308,7 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
|
||||
case let .channelPost(messageId):
|
||||
message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId)
|
||||
case let .groupMessage(messageId):
|
||||
message = .single(ChatReplyThreadMessage(
|
||||
messageId: messageId,
|
||||
maxMessage: .unknown,
|
||||
maxReadMessageId: nil
|
||||
))
|
||||
message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId)
|
||||
}
|
||||
|
||||
return message
|
||||
@ -341,7 +337,8 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
|
||||
threadMessageId: message.messageId,
|
||||
isChannelPost: isChannelPost,
|
||||
maxMessage: message.maxMessage,
|
||||
maxReadMessageId: message.maxReadMessageId
|
||||
maxReadIncomingMessageId: message.maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: message.maxReadOutgoingMessageId
|
||||
),
|
||||
chatLocationContextHolder: chatLocationContextHolder,
|
||||
fixedCombinedReadStates: nil,
|
||||
|
@ -134,6 +134,9 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS
|
||||
guard !chatPresentationInterfaceState.isScheduledMessages else {
|
||||
return false
|
||||
}
|
||||
guard !peer.id.isReplies else {
|
||||
return false
|
||||
}
|
||||
switch chatPresentationInterfaceState.mode {
|
||||
case .inline:
|
||||
return false
|
||||
@ -465,7 +468,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
}
|
||||
|
||||
var isReplyThreadHead = false
|
||||
if case let .replyThread(messageId, _, _, _) = chatPresentationInterfaceState.chatLocation {
|
||||
if case let .replyThread(messageId, _, _, _, _) = chatPresentationInterfaceState.chatLocation {
|
||||
isReplyThreadHead = messages[0].id == messageId
|
||||
}
|
||||
|
||||
@ -616,29 +619,25 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
}
|
||||
c.dismiss(completion: {
|
||||
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel {
|
||||
if case .group = channel.info {
|
||||
interfaceInteraction.viewReplies(messages[0].id, ChatReplyThreadMessage(messageId: replyThreadId, maxMessage: .unknown, maxReadMessageId: nil))
|
||||
} else {
|
||||
var cancelImpl: (() -> Void)?
|
||||
let statusController = OverlayStatusController(theme: chatPresentationInterfaceState.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
controllerInteraction.presentController(statusController, nil)
|
||||
var cancelImpl: (() -> Void)?
|
||||
let statusController = OverlayStatusController(theme: chatPresentationInterfaceState.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
controllerInteraction.presentController(statusController, nil)
|
||||
|
||||
let disposable = (foundIndex.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak statusController] result in
|
||||
statusController?.dismiss()
|
||||
|
||||
let disposable = (foundIndex.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak statusController] result in
|
||||
statusController?.dismiss()
|
||||
|
||||
if let result = result {
|
||||
interfaceInteraction.viewReplies(nil, result)
|
||||
}
|
||||
})
|
||||
|
||||
cancelImpl = { [weak statusController] in
|
||||
disposable.dispose()
|
||||
statusController?.dismiss()
|
||||
if let result = result {
|
||||
interfaceInteraction.viewReplies(nil, result)
|
||||
}
|
||||
})
|
||||
|
||||
cancelImpl = { [weak statusController] in
|
||||
disposable.dispose()
|
||||
statusController?.dismiss()
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -746,10 +745,10 @@ 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 {
|
||||
if case let .replyThread(replyThread, _, _, _, _) = chatPresentationInterfaceState.chatLocation {
|
||||
threadMessageId = replyThread
|
||||
}
|
||||
let _ = (exportMessageLink(account: context.account, peerId: message.id.peerId, messageId: message.id, threadMessageId: threadMessageId)
|
||||
let _ = (exportMessageLink(account: context.account, peerId: message.id.peerId, messageId: message.id, isThread: threadMessageId != nil)
|
||||
|> map { result -> String? in
|
||||
return result
|
||||
}
|
||||
@ -838,6 +837,12 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
}, action: { controller, f in
|
||||
interfaceInteraction.reportMessages(selectAll ? messages : [message], controller)
|
||||
})))
|
||||
} else if message.id.peerId.isReplies {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuBlock, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { controller, f in
|
||||
interfaceInteraction.blockMessageAuthor(message, controller)
|
||||
})))
|
||||
}
|
||||
|
||||
var clearCacheAsDelete = false
|
||||
|
@ -561,7 +561,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
} else if incoming {
|
||||
hasAvatar = true
|
||||
}
|
||||
case let .replyThread(messageId, isChannelPost, _, _):
|
||||
case let .replyThread(messageId, isChannelPost, _, _, _):
|
||||
if messageId.peerId != item.context.account.peerId {
|
||||
if messageId.peerId.isGroupOrChannel && item.message.author != nil {
|
||||
var isBroadcastChannel = false
|
||||
@ -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(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
|
@ -841,7 +841,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
switch item.chatLocation {
|
||||
case let .peer(peerId):
|
||||
chatLocationPeerId = peerId
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
chatLocationPeerId = messageId.peerId
|
||||
}
|
||||
|
||||
@ -885,7 +885,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
allowFullWidth = true
|
||||
}
|
||||
|
||||
if case let .replyThread(messageId, isChannelPost, _, _) = item.chatLocation, isChannelPost, messageId == firstMessage.id {
|
||||
if case let .replyThread(messageId, isChannelPost, _, _, _) = item.chatLocation, isChannelPost, messageId == firstMessage.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -1048,7 +1048,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(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == attribute.messageId {
|
||||
} else {
|
||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||
}
|
||||
|
@ -99,12 +99,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
||||
return
|
||||
}
|
||||
if item.message.id.peerId.isReplies {
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
|
||||
break
|
||||
}
|
||||
}
|
||||
item.controllerInteraction.openReplyThreadOriginalMessage(item.message)
|
||||
} else {
|
||||
item.controllerInteraction.openMessageReplies(item.message.id, true)
|
||||
}
|
||||
@ -145,8 +140,6 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
if let maxMessageId = attribute.maxMessageId, let maxReadMessageId = attribute.maxReadMessageId {
|
||||
hasUnseenReplies = maxMessageId > maxReadMessageId
|
||||
} else if attribute.maxMessageId != nil {
|
||||
hasUnseenReplies = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
switch item.chatLocation {
|
||||
case let .peer(peerId):
|
||||
messagePeerId = peerId
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
messagePeerId = messageId.peerId
|
||||
}
|
||||
|
||||
@ -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(messageId, isChannelPost, _, _, _) = item.chatLocation, isChannelPost, 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(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
||||
switch chatLocation {
|
||||
case let .peer(peerId):
|
||||
messagePeerId = peerId
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
messagePeerId = messageId.peerId
|
||||
}
|
||||
|
||||
@ -333,7 +333,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(messageId, isChannelPost, _, _, _) = chatLocation, isChannelPost, messageId == message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
if !hasActionMedia && !isBroadcastChannel {
|
||||
|
@ -249,7 +249,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
} else if incoming {
|
||||
hasAvatar = true
|
||||
}
|
||||
case let .replyThread(messageId, isChannelPost, _, _):
|
||||
case let .replyThread(messageId, isChannelPost, _, _, _):
|
||||
if messageId.peerId != item.context.account.peerId {
|
||||
if messageId.peerId.isGroupOrChannel && item.message.author != nil {
|
||||
var isBroadcastChannel = false
|
||||
@ -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(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
@ -480,6 +480,65 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
layoutSize.height += actionButtonsSizeAndApply.0.height
|
||||
}
|
||||
|
||||
var updatedImageFrame = imageFrame.offsetBy(dx: 0.0, dy: floor((contentHeight - imageSize.height) / 2.0))
|
||||
|
||||
var dateOffset = CGPoint(x: dateAndStatusSize.width + 4.0, y: dateAndStatusSize.height + 16.0)
|
||||
if isEmoji {
|
||||
if incoming {
|
||||
dateOffset.x = 12.0
|
||||
} else {
|
||||
dateOffset.y = 12.0
|
||||
}
|
||||
}
|
||||
var dateAndStatusFrame = CGRect(origin: CGPoint(x: min(layoutSize.width - dateAndStatusSize.width - 14.0, max(displayLeftInset, updatedImageFrame.maxX - dateOffset.x)), y: updatedImageFrame.maxY - dateOffset.y), size: dateAndStatusSize)
|
||||
|
||||
let baseShareButtonSize = CGSize(width: 30.0, height: 60.0)
|
||||
var baseShareButtonFrame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 6.0, y: updatedImageFrame.maxY - 10.0 - baseShareButtonSize.height - 4.0), size: baseShareButtonSize)
|
||||
if isEmoji && incoming {
|
||||
baseShareButtonFrame.origin.x = dateAndStatusFrame.maxX + 8.0
|
||||
}
|
||||
|
||||
var viaBotFrame: CGRect?
|
||||
if let (viaBotLayout, _) = viaBotApply {
|
||||
viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 15.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 14.0)), y: 8.0), size: viaBotLayout.size)
|
||||
}
|
||||
|
||||
var replyInfoFrame: CGRect?
|
||||
if let (replyInfoSize, _) = replyInfoApply {
|
||||
var viaBotSize = CGSize()
|
||||
if let viaBotFrame = viaBotFrame {
|
||||
viaBotSize = viaBotFrame.size
|
||||
}
|
||||
let replyInfoFrameValue = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize)
|
||||
replyInfoFrame = replyInfoFrameValue
|
||||
if let viaBotFrameValue = viaBotFrame {
|
||||
if replyInfoFrameValue.minX < replyInfoFrameValue.minX {
|
||||
viaBotFrame = viaBotFrameValue.offsetBy(dx: replyInfoFrameValue.minX - viaBotFrameValue.minX, dy: 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var replyBackgroundFrame: CGRect?
|
||||
if let replyInfoFrame = replyInfoFrame {
|
||||
var viaBotSize = CGSize()
|
||||
if let viaBotFrame = viaBotFrame {
|
||||
viaBotSize = viaBotFrame.size
|
||||
}
|
||||
|
||||
replyBackgroundFrame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
|
||||
}
|
||||
|
||||
if let replyBackgroundFrameValue = replyBackgroundFrame {
|
||||
if replyBackgroundFrameValue.insetBy(dx: -2.0, dy: -2.0).intersects(baseShareButtonFrame) {
|
||||
let offset: CGFloat = 25.0
|
||||
|
||||
layoutSize.height += offset
|
||||
updatedImageFrame.origin.y += offset
|
||||
dateAndStatusFrame.origin.y += offset
|
||||
baseShareButtonFrame.origin.y += offset
|
||||
}
|
||||
}
|
||||
|
||||
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _ in
|
||||
if let strongSelf = self {
|
||||
var transition: ContainedViewLayoutTransition = .immediate
|
||||
@ -487,7 +546,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
transition = .animated(duration: duration, curve: .spring)
|
||||
}
|
||||
|
||||
let updatedImageFrame = imageFrame.offsetBy(dx: 0.0, dy: floor((contentHeight - imageSize.height) / 2.0))
|
||||
transition.updateFrame(node: strongSelf.imageNode, frame: updatedImageFrame)
|
||||
imageApply()
|
||||
|
||||
@ -499,15 +557,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
|
||||
dateAndStatusApply(false)
|
||||
|
||||
var dateOffset = CGPoint(x: dateAndStatusSize.width + 4.0, y: dateAndStatusSize.height + 16.0)
|
||||
if isEmoji {
|
||||
if incoming {
|
||||
dateOffset.x = 12.0
|
||||
} else {
|
||||
dateOffset.y = 12.0
|
||||
}
|
||||
}
|
||||
let dateAndStatusFrame = CGRect(origin: CGPoint(x: min(layoutSize.width - dateAndStatusSize.width - 14.0, max(displayLeftInset, updatedImageFrame.maxX - dateOffset.x)), y: updatedImageFrame.maxY - dateOffset.y), size: dateAndStatusSize)
|
||||
transition.updateFrame(node: strongSelf.dateAndStatusNode, frame: dateAndStatusFrame)
|
||||
|
||||
if let updatedShareButtonNode = updatedShareButtonNode {
|
||||
@ -520,10 +569,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
||||
var shareButtonFrame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 6.0, y: updatedImageFrame.maxY - 10.0 - buttonSize.height - 4.0), size: buttonSize)
|
||||
if isEmoji && incoming {
|
||||
shareButtonFrame.origin.x = dateAndStatusFrame.maxX + 8.0
|
||||
}
|
||||
let shareButtonFrame = CGRect(origin: CGPoint(x: baseShareButtonFrame.minX, y: baseShareButtonFrame.maxY - buttonSize.height), size: buttonSize)
|
||||
transition.updateFrame(node: updatedShareButtonNode, frame: shareButtonFrame)
|
||||
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
||||
shareButtonNode.removeFromSupernode()
|
||||
@ -543,13 +589,12 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.replyBackgroundNode = nil
|
||||
}
|
||||
|
||||
if let (viaBotLayout, viaBotApply) = viaBotApply {
|
||||
if let (_, viaBotApply) = viaBotApply, let viaBotFrame = viaBotFrame {
|
||||
let viaBotNode = viaBotApply()
|
||||
if strongSelf.viaBotNode == nil {
|
||||
strongSelf.viaBotNode = viaBotNode
|
||||
strongSelf.addSubnode(viaBotNode)
|
||||
}
|
||||
let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 15.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 14.0)), y: 8.0), size: viaBotLayout.size)
|
||||
viaBotNode.frame = viaBotFrame
|
||||
strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 6.0, y: viaBotFrame.minY - 2.0 - UIScreenPixel), size: CGSize(width: viaBotFrame.size.width + 11.0, height: viaBotFrame.size.height + 5.0))
|
||||
} else if let viaBotNode = strongSelf.viaBotNode {
|
||||
@ -557,24 +602,14 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.viaBotNode = nil
|
||||
}
|
||||
|
||||
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
|
||||
if let (_, replyInfoApply) = replyInfoApply, let replyInfoFrame = replyInfoFrame {
|
||||
let replyInfoNode = replyInfoApply()
|
||||
if strongSelf.replyInfoNode == nil {
|
||||
strongSelf.replyInfoNode = replyInfoNode
|
||||
strongSelf.addSubnode(replyInfoNode)
|
||||
}
|
||||
var viaBotSize = CGSize()
|
||||
if let viaBotNode = strongSelf.viaBotNode {
|
||||
viaBotSize = viaBotNode.frame.size
|
||||
}
|
||||
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize)
|
||||
if let viaBotNode = strongSelf.viaBotNode {
|
||||
if replyInfoFrame.minX < viaBotNode.frame.minX {
|
||||
viaBotNode.frame = viaBotNode.frame.offsetBy(dx: replyInfoFrame.minX - viaBotNode.frame.minX, dy: 0.0)
|
||||
}
|
||||
}
|
||||
replyInfoNode.frame = replyInfoFrame
|
||||
strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0))
|
||||
strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame ?? CGRect()
|
||||
|
||||
if let _ = item.controllerInteraction.selectionState, isEmoji {
|
||||
replyInfoNode.alpha = 0.0
|
||||
|
@ -56,6 +56,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
let deleteSelectedMessages: () -> Void
|
||||
let reportSelectedMessages: () -> Void
|
||||
let reportMessages: ([Message], ContextController?) -> Void
|
||||
let blockMessageAuthor: (Message, ContextController?) -> Void
|
||||
let deleteMessages: ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void
|
||||
let forwardSelectedMessages: () -> Void
|
||||
let forwardCurrentForwardMessages: () -> Void
|
||||
@ -130,6 +131,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
deleteSelectedMessages: @escaping () -> Void,
|
||||
reportSelectedMessages: @escaping () -> Void,
|
||||
reportMessages: @escaping ([Message], ContextController?) -> Void,
|
||||
blockMessageAuthor: @escaping (Message, ContextController?) -> Void,
|
||||
deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void,
|
||||
forwardSelectedMessages: @escaping () -> Void,
|
||||
forwardCurrentForwardMessages: @escaping () -> Void,
|
||||
@ -203,6 +205,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
self.deleteSelectedMessages = deleteSelectedMessages
|
||||
self.reportSelectedMessages = reportSelectedMessages
|
||||
self.reportMessages = reportMessages
|
||||
self.blockMessageAuthor = blockMessageAuthor
|
||||
self.deleteMessages = deleteMessages
|
||||
self.forwardSelectedMessages = forwardSelectedMessages
|
||||
self.forwardCurrentForwardMessages = forwardCurrentForwardMessages
|
||||
|
@ -58,6 +58,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
||||
}, deleteSelectedMessages: {
|
||||
}, reportSelectedMessages: {
|
||||
}, reportMessages: { _, _ in
|
||||
}, blockMessageAuthor: { _, _ in
|
||||
}, deleteMessages: { _, _, f in
|
||||
f(.default)
|
||||
}, forwardSelectedMessages: {
|
||||
|
@ -452,6 +452,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
return nil
|
||||
}, openPeerContextMenu: { _, _, _, _ in
|
||||
}, openMessageReplies: { _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||
@ -816,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, maxReadMessageId, messageId):
|
||||
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId), subject: .message(messageId)))
|
||||
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)))
|
||||
}
|
||||
case let .stickerPack(name):
|
||||
let packReference: StickerPackReference = .name(name)
|
||||
|
@ -792,7 +792,7 @@ 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 {
|
||||
} else if case let .replyThread(_, isChannelPost, _, _, _) = interfaceState.chatLocation {
|
||||
if isChannelPost {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderComment
|
||||
} else {
|
||||
|
@ -205,6 +205,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
inputActivitiesAllowed = false
|
||||
}
|
||||
}
|
||||
case .replyThread:
|
||||
inputActivitiesAllowed = true
|
||||
default:
|
||||
inputActivitiesAllowed = false
|
||||
}
|
||||
@ -402,7 +404,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
|
||||
if self.activityNode.transitionToState(state, animation: .slide) {
|
||||
self.setNeedsLayout()
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -549,7 +553,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
if titleFrame.size.width < size.width {
|
||||
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
|
||||
}
|
||||
self.titleNode.frame = titleFrame
|
||||
transition.updateFrameAdditiveToCenter(node: self.titleNode, frame: titleFrame)
|
||||
} else {
|
||||
let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing
|
||||
|
||||
@ -558,7 +562,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
|
||||
}
|
||||
titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth)
|
||||
self.titleNode.frame = titleFrame
|
||||
transition.updateFrameAdditiveToCenter(node: self.titleNode, frame: titleFrame)
|
||||
|
||||
var activityFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize)
|
||||
if activitySize.width < size.width {
|
||||
@ -584,7 +588,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing
|
||||
|
||||
titleFrame = CGRect(origin: CGPoint(x: leftIconWidth + floor((clearBounds.width - combinedWidth) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
self.titleNode.frame = titleFrame
|
||||
transition.updateFrameAdditiveToCenter(node: self.titleNode, frame: titleFrame)
|
||||
self.activityNode.frame = CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize)
|
||||
|
||||
if let image = self.titleLeftIconNode.image {
|
||||
|
@ -145,6 +145,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
return nil
|
||||
}, openPeerContextMenu: { _, _, _, _ in
|
||||
}, openMessageReplies: { _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -125,7 +125,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
||||
if message.id.peerId == peerId {
|
||||
return true
|
||||
}
|
||||
case let .replyThread(messageId, _, _, _):
|
||||
case let .replyThread(messageId, _, _, _, _):
|
||||
if message.id.peerId == messageId.peerId {
|
||||
return true
|
||||
}
|
||||
|
@ -90,9 +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, maxReadMessageId, messageId):
|
||||
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
|
||||
if let navigationController = navigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId), subject: .message(messageId)))
|
||||
ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in
|
||||
present(c, a)
|
||||
}, messageId: replyThreadMessageId, isChannelPost: isChannelPost, atMessage: messageId)
|
||||
}
|
||||
case let .stickerPack(name):
|
||||
dismissInput()
|
||||
|
@ -134,6 +134,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
return nil
|
||||
}, openPeerContextMenu: { _, _, _, _ in
|
||||
}, openMessageReplies: { _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||
|
@ -361,6 +361,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||
}, reportSelectedMessages: {
|
||||
reportMessages()
|
||||
}, reportMessages: { _, _ in
|
||||
}, blockMessageAuthor: { _, _ in
|
||||
}, deleteMessages: { _, _, f in
|
||||
f(.default)
|
||||
}, forwardSelectedMessages: {
|
||||
@ -1957,6 +1958,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
return nil
|
||||
}, openPeerContextMenu: { _, _, _, _ in
|
||||
}, openMessageReplies: { _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -1199,6 +1199,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return nil
|
||||
}, openPeerContextMenu: { _, _, _, _ in
|
||||
}, openMessageReplies: { _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
|
@ -59,9 +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, maxReadMessageId, messageId):
|
||||
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxMessage: maxMessage, maxReadMessageId: maxReadMessageId), subject: .message(messageId)))
|
||||
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)
|
||||
}
|
||||
case let .stickerPack(name):
|
||||
let packReference: StickerPackReference = .name(name)
|
||||
|
@ -160,7 +160,9 @@ static void setBoolField(CustomBlurEffect *object, NSString *name, BOOL value) {
|
||||
}
|
||||
|
||||
UIBlurEffect *makeCustomZoomBlurEffectImpl() {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
return [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemUltraThinMaterialLight];
|
||||
} else if (@available(iOS 11.0, *)) {
|
||||
NSString *string = [@[@"_", @"UI", @"Custom", @"BlurEffect"] componentsJoinedByString:@""];
|
||||
CustomBlurEffect *result = (CustomBlurEffect *)[NSClassFromString(string) effectWithStyle:0];
|
||||
|
||||
|
@ -25,7 +25,7 @@ public enum ParsedInternalPeerUrlParameter {
|
||||
public enum ParsedInternalUrl {
|
||||
case peerName(String, ParsedInternalPeerUrlParameter?)
|
||||
case peerId(PeerId)
|
||||
case privateMessage(MessageId)
|
||||
case privateMessage(messageId: MessageId, threadId: Int32?)
|
||||
case stickerPack(String)
|
||||
case join(String)
|
||||
case localization(String)
|
||||
@ -240,16 +240,35 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
return .theme(pathComponents[1])
|
||||
} else if pathComponents.count == 3 && pathComponents[0] == "c" {
|
||||
if let channelId = Int32(pathComponents[1]), let messageId = Int32(pathComponents[2]) {
|
||||
return .privateMessage(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: messageId))
|
||||
var threadId: Int32?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "thread" {
|
||||
if let intValue = Int32(value) {
|
||||
threadId = intValue
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return .privateMessage(messageId: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: messageId), threadId: threadId)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if let value = Int(pathComponents[1]) {
|
||||
var threadId: Int32?
|
||||
var commentId: Int32?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "comment" {
|
||||
if queryItem.name == "thread" {
|
||||
if let intValue = Int32(value) {
|
||||
threadId = intValue
|
||||
break
|
||||
}
|
||||
} else if queryItem.name == "comment" {
|
||||
if let intValue = Int32(value) {
|
||||
commentId = intValue
|
||||
break
|
||||
@ -258,7 +277,9 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let commentId = commentId {
|
||||
if let threadId = threadId {
|
||||
return .peerName(peerName, .replyThread(threadId, Int32(value)))
|
||||
} else if let commentId = commentId {
|
||||
return .peerName(peerName, .replyThread(Int32(value), commentId))
|
||||
} else {
|
||||
return .peerName(peerName, .channelMessage(Int32(value)))
|
||||
@ -305,7 +326,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
|
||||
guard let result = result else {
|
||||
return .channelMessage(peerId: peer.id, messageId: replyThreadMessageId)
|
||||
}
|
||||
return .replyThreadMessage(replyThreadMessageId: result.messageId, isChannelPost: true, maxMessage: result.maxMessage, maxReadMessageId: result.maxReadMessageId, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))
|
||||
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))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -330,21 +351,34 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
|
||||
return .single(.inaccessiblePeer)
|
||||
}
|
||||
}
|
||||
case let .privateMessage(messageId):
|
||||
case let .privateMessage(messageId, threadId):
|
||||
return account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(messageId.peerId)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
let foundPeer: Signal<Peer?, NoError>
|
||||
if let peer = peer {
|
||||
return .single(.peer(peer.id, .chat(textInputState: nil, subject: .message(messageId), peekData: nil)))
|
||||
foundPeer = .single(peer)
|
||||
} else {
|
||||
return findChannelById(postbox: account.postbox, network: account.network, channelId: messageId.peerId.id)
|
||||
|> map { foundPeer -> ResolvedUrl? in
|
||||
if let foundPeer = foundPeer {
|
||||
return .peer(foundPeer.id, .chat(textInputState: nil, subject: .message(messageId), peekData: nil))
|
||||
foundPeer = findChannelById(postbox: account.postbox, network: account.network, channelId: messageId.peerId.id)
|
||||
}
|
||||
return foundPeer
|
||||
|> mapToSignal { foundPeer -> Signal<ResolvedUrl?, NoError> in
|
||||
if let foundPeer = foundPeer {
|
||||
if let threadId = threadId {
|
||||
let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId)
|
||||
return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId)
|
||||
|> 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)
|
||||
}
|
||||
} else {
|
||||
return .inaccessiblePeer
|
||||
return .single(.peer(foundPeer.id, .chat(textInputState: nil, subject: .message(messageId), peekData: nil)))
|
||||
}
|
||||
} else {
|
||||
return .single(.inaccessiblePeer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
|
||||
public var Wallet_Send_ConfirmationConfirm: String { return self._s[218]! }
|
||||
public var Wallet_Created_ExportErrorTitle: String { return self._s[219]! }
|
||||
public var Wallet_Info_TransactionPendingHeader: String { return self._s[220]! }
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
}
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
|
Loading…
x
Reference in New Issue
Block a user