Comments update [skip ci]

This commit is contained in:
Ali 2020-09-18 19:17:48 +04:00
parent c378d634c5
commit 0eccec10ed
53 changed files with 5307 additions and 4435 deletions

View File

@ -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";

View File

@ -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)
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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: &currentUnsentOperations, 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: &currentUnsentOperations, 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

View File

@ -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
}
}

View File

@ -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:

View File

@ -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:

View File

@ -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?)

View File

@ -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>) {

View File

@ -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)
}

View File

@ -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 }))
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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] = []

View File

@ -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)
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}
)

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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,

View File

@ -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:

View File

@ -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
}
}

View File

@ -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,

View File

@ -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

View File

@ -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))
}

View File

@ -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]
}

View File

@ -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
}
}
}

View File

@ -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))
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -58,6 +58,7 @@ final class ChatRecentActionsController: TelegramBaseController {
}, deleteSelectedMessages: {
}, reportSelectedMessages: {
}, reportMessages: { _, _ in
}, blockMessageAuthor: { _, _ in
}, deleteMessages: { _, _, f in
f(.default)
}, forwardSelectedMessages: {

View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -145,6 +145,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
return nil
}, openPeerContextMenu: { _, _, _, _ in
}, openMessageReplies: { _, _ in
}, openReplyThreadOriginalMessage: { _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,

View File

@ -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
}

View File

@ -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()

View File

@ -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))

View File

@ -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,

View File

@ -1199,6 +1199,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return nil
}, openPeerContextMenu: { _, _, _, _ in
}, openMessageReplies: { _, _ in
}, openReplyThreadOriginalMessage: { _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,

View File

@ -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)

View File

@ -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];

View File

@ -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)
}
}
}

View File

@ -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)