mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
no message
This commit is contained in:
parent
4447916713
commit
0bdb130775
55 changed files with 1161 additions and 277 deletions
|
|
@ -829,7 +829,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
}
|
||||
|
||||
var joined = false
|
||||
if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first {
|
||||
if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first {
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, action.action == .peerJoined {
|
||||
joined = true
|
||||
|
|
@ -843,7 +843,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
chatListController.navigationPresentation = .master
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _):
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, promoInfo, _, _, _):
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
|
|
@ -1783,7 +1783,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
public private(set) var isSearchActive: Bool = false
|
||||
public func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil) {
|
||||
if self.displayNavigationBar {
|
||||
let _ = (combineLatest(self.chatListDisplayNode.containerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(tagSummary: nil, actionsSummary: nil)) |> take(1))
|
||||
let _ = (combineLatest(self.chatListDisplayNode.containerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _, chatListView in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
|||
)
|
||||
let readState = EnginePeerReadCounters()
|
||||
|
||||
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
}
|
||||
|
||||
var itemNodes: [ChatListItemNode] = []
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
|||
if let tagMask = tagMask, tagMask != .photoOrVideo {
|
||||
return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings())), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peer.peerId), interaction: listInteraction, message: message._asMessage(), selection: selection, displayHeader: enableHeaders && !displayCustomHeader, customHeader: nil, hintIsLink: tagMask == .webPage, isGlobalSearchResult: true)
|
||||
} else {
|
||||
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
}
|
||||
case let .addContact(phoneNumber, theme, strings):
|
||||
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||
|
|
@ -1237,7 +1237,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||
return
|
||||
}
|
||||
switch item.content {
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if let peer = peer.peer, let message = messages.first {
|
||||
peerContextAction(peer, .search(message.id), node, gesture)
|
||||
}
|
||||
|
|
@ -2171,7 +2171,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||
bounds = selectedItemNode.bounds
|
||||
}
|
||||
switch item.content {
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return (selectedItemNode.view, bounds, messages.last?.id ?? peer.peerId)
|
||||
case let .groupReference(groupId, _, _, _, _):
|
||||
return (selectedItemNode.view, bounds, groupId)
|
||||
|
|
@ -2358,7 +2358,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
|||
associatedMessageIds: []
|
||||
)
|
||||
let readState = EnginePeerReadCounters()
|
||||
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
case .media:
|
||||
return nil
|
||||
case .links:
|
||||
|
|
|
|||
|
|
@ -36,12 +36,12 @@ public enum ChatListItemContent {
|
|||
}
|
||||
}
|
||||
|
||||
case peer(messages: [EngineMessage], peer: EngineRenderedPeer, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool)
|
||||
case peer(messages: [EngineMessage], peer: EngineRenderedPeer, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool)
|
||||
case groupReference(groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool)
|
||||
|
||||
public var chatLocation: ChatLocation? {
|
||||
switch self {
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return .peer(peer.peerId)
|
||||
case .groupReference:
|
||||
return nil
|
||||
|
|
@ -140,7 +140,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
|
|||
|
||||
public func selected(listView: ListView) {
|
||||
switch self.content {
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, promoInfo, _, _, _):
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, promoInfo, _, _, _):
|
||||
if let message = messages.last, let peer = peer.peer {
|
||||
self.interaction.messageSelected(peer, message, promoInfo)
|
||||
} else if let peer = peer.peer {
|
||||
|
|
@ -501,7 +501,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(Int32(allCount)))"
|
||||
}
|
||||
return result
|
||||
case let .peer(_, peer, combinedReadState, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(_, peer, combinedReadState, _, _, _, _, _, _, _, _, _, _):
|
||||
guard let chatMainPeer = peer.chatMainPeer else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -561,7 +561,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
} else {
|
||||
return item.presentationData.strings.VoiceOver_ChatList_MessageEmpty
|
||||
}
|
||||
case let .peer(messages, peer, combinedReadState, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(messages, peer, combinedReadState, _, _, _, _, _, _, _, _, _, _):
|
||||
if let message = messages.last {
|
||||
var result = ""
|
||||
if message.flags.contains(.Incoming) {
|
||||
|
|
@ -683,7 +683,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
var displayAsMessage = false
|
||||
var enablePreview = true
|
||||
switch item.content {
|
||||
case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, displayAsMessageValue, _):
|
||||
case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, _, displayAsMessageValue, _):
|
||||
displayAsMessage = displayAsMessageValue
|
||||
if displayAsMessage, case let .user(author) = messages.last?.author {
|
||||
peer = .user(author)
|
||||
|
|
@ -790,7 +790,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
guard let item = self.item, item.editing else {
|
||||
return
|
||||
}
|
||||
if case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _) = item.content {
|
||||
if case let .peer(_, peer, _, _, _, _, _, _, _, promoInfo, _, _, _) = item.content {
|
||||
if promoInfo == nil, let mainPeer = peer.peer {
|
||||
item.interaction.togglePeerSelected(mainPeer)
|
||||
}
|
||||
|
|
@ -833,6 +833,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
let peerPresence: EnginePeer.Presence?
|
||||
let draftState: ChatListItemContent.DraftState?
|
||||
let hasUnseenMentions: Bool
|
||||
let hasUnseenReactions: Bool
|
||||
let inputActivities: [(EnginePeer, PeerInputActivity)]?
|
||||
let isPeerGroup: Bool
|
||||
let promoInfo: ChatListNodeEntryPromoInfo?
|
||||
|
|
@ -842,7 +843,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
var groupHiddenByDefault = false
|
||||
|
||||
switch item.content {
|
||||
case let .peer(messagesValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, hasUnseenMentionsValue, draftStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _):
|
||||
case let .peer(messagesValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, hasUnseenMentionsValue, hasUnseenReactionsValue, draftStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _):
|
||||
messages = messagesValue
|
||||
contentPeer = .chat(peerValue)
|
||||
combinedReadState = combinedReadStateValue
|
||||
|
|
@ -861,6 +862,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
}
|
||||
draftState = draftStateValue
|
||||
hasUnseenMentions = hasUnseenMentionsValue
|
||||
hasUnseenReactions = hasUnseenReactionsValue
|
||||
|
||||
switch peerValue.peer {
|
||||
case .user, .secretChat:
|
||||
|
|
@ -892,6 +894,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
isRemovedFromTotalUnreadCount = false
|
||||
draftState = nil
|
||||
hasUnseenMentions = false
|
||||
hasUnseenReactions = false
|
||||
inputActivities = nil
|
||||
isPeerGroup = true
|
||||
groupHiddenByDefault = hiddenByDefault
|
||||
|
|
@ -1304,7 +1307,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
}
|
||||
|
||||
if !isPeerGroup {
|
||||
if hasUnseenMentions {
|
||||
if hasUnseenMentions || hasUnseenReactions {
|
||||
if case .archive = item.peerGroupId {
|
||||
currentMentionBadgeImage = PresentationResourcesChatList.badgeBackgroundInactiveMention(item.presentationData.theme, diameter: badgeDiameter)
|
||||
} else {
|
||||
|
|
@ -1344,7 +1347,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
if !isPeerGroup {
|
||||
if displayAsMessage {
|
||||
switch item.content {
|
||||
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if let peer = messages.last?.author {
|
||||
if peer.isScam {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||
|
|
@ -1448,7 +1451,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
let peerRevealOptions: [ItemListRevealOption]
|
||||
let peerLeftRevealOptions: [ItemListRevealOption]
|
||||
switch item.content {
|
||||
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage, _):
|
||||
case let .peer(_, renderedPeer, _, _, presence, _, _, _, _, _, _, displayAsMessage, _):
|
||||
if !displayAsMessage {
|
||||
if case let .user(peer) = renderedPeer.chatMainPeer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
|
||||
let updatedPresence = EnginePeer.Presence(status: presence.status, lastActivity: 0)
|
||||
|
|
@ -2143,7 +2146,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
close = false
|
||||
case RevealOptionKey.delete.rawValue:
|
||||
var joined = false
|
||||
if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first {
|
||||
if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first {
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, action.action == .peerJoined {
|
||||
joined = true
|
||||
|
|
@ -2179,7 +2182,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
item.interaction.toggleArchivedFolderHiddenByDefault()
|
||||
close = false
|
||||
case RevealOptionKey.hidePsa.rawValue:
|
||||
if let item = self.item, case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _) = item.content {
|
||||
if let item = self.item, case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _) = item.content {
|
||||
item.interaction.hidePsa(peer.peerId)
|
||||
}
|
||||
close = false
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
|||
nodeInteraction.additionalCategorySelected(id)
|
||||
}
|
||||
), directionHint: entry.directionHint)
|
||||
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, presence, hasUnseenMentions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
|
||||
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, presence, hasUnseenMentions, hasUnseenReactions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
|
||||
switch mode {
|
||||
case .chatList:
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
|
||||
|
|
@ -226,6 +226,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
|||
isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount,
|
||||
presence: presence,
|
||||
hasUnseenMentions: hasUnseenMentions,
|
||||
hasUnseenReactions: hasUnseenReactions,
|
||||
draftState: draftState,
|
||||
inputActivities: inputActivities,
|
||||
promoInfo: promoInfo,
|
||||
|
|
@ -396,7 +397,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
|||
private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: EngineChatList.Group, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
|
||||
return entries.map { entry -> ListViewUpdateItem in
|
||||
switch entry.entry {
|
||||
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, presence, hasUnseenMentions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
|
||||
case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, presence, hasUnseenMentions, hasUnseenReactions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact):
|
||||
switch mode {
|
||||
case .chatList:
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
|
||||
|
|
@ -412,6 +413,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
|||
isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount,
|
||||
presence: presence,
|
||||
hasUnseenMentions: hasUnseenMentions,
|
||||
hasUnseenReactions: hasUnseenReactions,
|
||||
draftState: draftState,
|
||||
inputActivities: inputActivities,
|
||||
promoInfo: promoInfo,
|
||||
|
|
@ -967,7 +969,7 @@ public final class ChatListNode: ListView {
|
|||
let (rawEntries, isLoading) = chatListNodeEntriesForView(EngineChatList(update.view), state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode)
|
||||
let entries = rawEntries.filter { entry in
|
||||
switch entry {
|
||||
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _):
|
||||
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
switch mode {
|
||||
case .chatList:
|
||||
return true
|
||||
|
|
@ -1078,7 +1080,7 @@ public final class ChatListNode: ListView {
|
|||
var didIncludeHiddenByDefaultArchive = false
|
||||
if let previous = previousView {
|
||||
for entry in previous.filteredEntries {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||
if index.pinningIndex != nil {
|
||||
previousPinnedChats.append(index.messageIndex.id.peerId)
|
||||
}
|
||||
|
|
@ -1094,7 +1096,7 @@ public final class ChatListNode: ListView {
|
|||
var doesIncludeArchive = false
|
||||
var doesIncludeHiddenByDefaultArchive = false
|
||||
for entry in processedView.filteredEntries {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||
if index.pinningIndex != nil {
|
||||
updatedPinnedChats.append(index.messageIndex.id.peerId)
|
||||
}
|
||||
|
|
@ -1325,7 +1327,7 @@ public final class ChatListNode: ListView {
|
|||
var referenceId: EngineChatList.PinnedItem.Id?
|
||||
var beforeAll = false
|
||||
switch toEntry {
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _):
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _):
|
||||
if promoInfo != nil {
|
||||
beforeAll = true
|
||||
} else {
|
||||
|
|
@ -1350,7 +1352,7 @@ public final class ChatListNode: ListView {
|
|||
|
||||
var itemId: EngineChatList.PinnedItem.Id?
|
||||
switch fromEntry {
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
itemId = .peer(index.messageIndex.id.peerId)
|
||||
default:
|
||||
break
|
||||
|
|
@ -1599,7 +1601,7 @@ public final class ChatListNode: ListView {
|
|||
if transition.chatListView.originalView.laterIndex == nil {
|
||||
for entry in filteredEntries.reversed() {
|
||||
switch entry {
|
||||
case let .PeerEntry(index, _, _, combinedReadState, isMuted, _, _, _, _, _, _, _, _, promoInfo, _, _):
|
||||
case let .PeerEntry(index, _, _, combinedReadState, isMuted, _, _, _, _, _, _, _, _, _, promoInfo, _, _):
|
||||
if promoInfo == nil {
|
||||
var hasUnread = false
|
||||
if let combinedReadState = combinedReadState {
|
||||
|
|
@ -1720,7 +1722,7 @@ public final class ChatListNode: ListView {
|
|||
for item in transition.insertItems {
|
||||
if let item = item.item as? ChatListItem {
|
||||
switch item.content {
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _):
|
||||
insertedPeerIds.append(peer.peerId)
|
||||
case .groupReference:
|
||||
break
|
||||
|
|
@ -1935,7 +1937,7 @@ public final class ChatListNode: ListView {
|
|||
continue
|
||||
}
|
||||
switch chatListView.filteredEntries[entryCount - i - 1] {
|
||||
case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _):
|
||||
case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
if interaction.highlightedChatLocation?.location == ChatLocation.peer(peer.peerId) {
|
||||
current = (index, peer.peer!, entryCount - i - 1)
|
||||
break outer
|
||||
|
|
@ -1981,10 +1983,10 @@ public final class ChatListNode: ListView {
|
|||
case .previous(unread: false), .next(unread: false):
|
||||
var target: (EngineChatList.Item.Index, EnginePeer)? = nil
|
||||
if let current = current, entryCount > 1 {
|
||||
if current.2 > 0, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] {
|
||||
if current.2 > 0, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] {
|
||||
next = (index, peer.peer!)
|
||||
}
|
||||
if current.2 <= entryCount - 2, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] {
|
||||
if current.2 <= entryCount - 2, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] {
|
||||
previous = (index, peer.peer!)
|
||||
}
|
||||
if case .previous = option {
|
||||
|
|
@ -1993,7 +1995,7 @@ public final class ChatListNode: ListView {
|
|||
target = next
|
||||
}
|
||||
} else if entryCount > 0 {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[entryCount - 1] {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[entryCount - 1] {
|
||||
target = (index, peer.peer!)
|
||||
}
|
||||
}
|
||||
|
|
@ -2069,7 +2071,7 @@ public final class ChatListNode: ListView {
|
|||
continue
|
||||
}
|
||||
switch chatListView.filteredEntries[entryCount - i - 1] {
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return index
|
||||
default:
|
||||
break
|
||||
|
|
@ -2085,7 +2087,7 @@ public final class ChatListNode: ListView {
|
|||
if resultPeer == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) {
|
||||
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
|
||||
switch item.content {
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _):
|
||||
resultPeer = peer.peer
|
||||
default:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public enum ChatListNodeEntryPromoInfo: Equatable {
|
|||
|
||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
case HeaderEntry
|
||||
case PeerEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, messages: [EngineMessage], readState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, draftState: ChatListItemContent.DraftState?, peer: EngineRenderedPeer, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool)
|
||||
case PeerEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, messages: [EngineMessage], readState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, draftState: ChatListItemContent.DraftState?, peer: EngineRenderedPeer, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool)
|
||||
case HoleEntry(EngineMessage.Index, theme: PresentationTheme)
|
||||
case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool)
|
||||
case ArchiveIntro(presentationData: ChatListPresentationData)
|
||||
|
|
@ -55,7 +55,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||
switch self {
|
||||
case .HeaderEntry:
|
||||
return .index(EngineChatList.Item.Index.absoluteUpperBound)
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return .index(index)
|
||||
case let .HoleEntry(holeIndex, _):
|
||||
return .index(EngineChatList.Item.Index(pinningIndex: nil, messageIndex: holeIndex))
|
||||
|
|
@ -72,7 +72,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||
switch self {
|
||||
case .HeaderEntry:
|
||||
return .Header
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return .PeerId(index.messageIndex.id.peerId.toInt64())
|
||||
case let .HoleEntry(holeIndex, _):
|
||||
return .Hole(Int64(holeIndex.id.id))
|
||||
|
|
@ -97,9 +97,9 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessages, lhsUnreadCount, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsSummaryInfo, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd, lhsHasFailedMessages, lhsIsContact):
|
||||
case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessages, lhsUnreadCount, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsHasUnseenMentions, lhsHasUnseenReactions, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd, lhsHasFailedMessages, lhsIsContact):
|
||||
switch rhs {
|
||||
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessages, rhsUnreadCount, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsSummaryInfo, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd, rhsHasFailedMessages, rhsIsContact):
|
||||
case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessages, rhsUnreadCount, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsHasUnseenMentions, rhsHasUnseenReactions, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd, rhsHasFailedMessages, rhsIsContact):
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
|
|
@ -161,7 +161,10 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
|||
if lhsPeer != rhsPeer {
|
||||
return false
|
||||
}
|
||||
if lhsSummaryInfo != rhsSummaryInfo {
|
||||
if lhsHasUnseenMentions != rhsHasUnseenMentions {
|
||||
return false
|
||||
}
|
||||
if lhsHasUnseenReactions != rhsHasUnseenReactions {
|
||||
return false
|
||||
}
|
||||
if let lhsInputActivities = lhsInputActivities, let rhsInputActivities = rhsInputActivities {
|
||||
|
|
@ -325,7 +328,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
|||
draftState = ChatListItemContent.DraftState(text: draftText)
|
||||
}
|
||||
|
||||
result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, editing: state.editing, hasActiveRevealControls: entry.index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(entry.index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[entry.index.messageIndex.id.peerId], promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact))
|
||||
result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, hasUnseenReactions: entry.hasUnseenReactions, editing: state.editing, hasActiveRevealControls: entry.index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(entry.index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[entry.index.messageIndex.id.peerId], promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact))
|
||||
}
|
||||
if !view.hasLater {
|
||||
var pinningIndex: UInt16 = UInt16(pinnedIndexOffset == 0 ? 0 : (pinnedIndexOffset - 1))
|
||||
|
|
@ -350,6 +353,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
|||
peer: EngineRenderedPeer(peerId: peer.0.id, peers: peers),
|
||||
presence: nil,
|
||||
hasUnseenMentions: false,
|
||||
hasUnseenReactions: false,
|
||||
editing: state.editing,
|
||||
hasActiveRevealControls: false,
|
||||
selected: state.selectedPeerIds.contains(peer.0.id),
|
||||
|
|
@ -364,7 +368,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
|||
}
|
||||
}
|
||||
|
||||
result.append(.PeerEntry(index: EngineChatList.Item.Index.absoluteUpperBound.predecessor, presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, draftState: nil, peer: EngineRenderedPeer(peerId: savedMessagesPeer.id, peers: [savedMessagesPeer.id: savedMessagesPeer]), presence: nil, hasUnseenMentions: false, editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(savedMessagesPeer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false))
|
||||
result.append(.PeerEntry(index: EngineChatList.Item.Index.absoluteUpperBound.predecessor, presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, draftState: nil, peer: EngineRenderedPeer(peerId: savedMessagesPeer.id, peers: [savedMessagesPeer.id: savedMessagesPeer]), presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(savedMessagesPeer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false))
|
||||
} else {
|
||||
if !filteredAdditionalItemEntries.isEmpty {
|
||||
for item in filteredAdditionalItemEntries.reversed() {
|
||||
|
|
@ -386,6 +390,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
|||
peer: item.item.renderedPeer,
|
||||
presence: item.item.presence,
|
||||
hasUnseenMentions: item.item.hasUnseenMentions,
|
||||
hasUnseenReactions: item.item.hasUnseenReactions,
|
||||
editing: state.editing,
|
||||
hasActiveRevealControls: item.item.index.messageIndex.id.peerId == state.peerIdWithRevealedOptions,
|
||||
selected: state.selectedPeerIds.contains(item.item.index.messageIndex.id.peerId),
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||
highlightedActionNode.performAction()
|
||||
}
|
||||
if let highlightedReaction = strongSelf.highlightedReaction {
|
||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction)
|
||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction, isLarge: false)
|
||||
}
|
||||
} else {
|
||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||
|
|
@ -481,7 +481,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||
}
|
||||
|
||||
if let highlightedReaction = strongSelf.highlightedReaction {
|
||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction)
|
||||
strongSelf.reactionContextNode?.performReactionSelection(reaction: highlightedReaction, isLarge: false)
|
||||
}
|
||||
} else {
|
||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||
|
|
@ -1340,11 +1340,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||
self.reactionContextNode = reactionContextNode
|
||||
self.addSubnode(reactionContextNode)
|
||||
|
||||
reactionContextNode.reactionSelected = { [weak self] reaction in
|
||||
reactionContextNode.reactionSelected = { [weak self] reaction, isLarge in
|
||||
guard let strongSelf = self, let controller = strongSelf.getController() as? ContextController else {
|
||||
return
|
||||
}
|
||||
controller.reactionSelected?(reaction)
|
||||
controller.reactionSelected?(reaction, isLarge)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2226,7 +2226,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
|||
|
||||
private var shouldBeDismissedDisposable: Disposable?
|
||||
|
||||
public var reactionSelected: ((ReactionContextItem) -> Void)?
|
||||
public var reactionSelected: ((ReactionContextItem, Bool) -> Void)?
|
||||
|
||||
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil) {
|
||||
self.account = account
|
||||
|
|
|
|||
|
|
@ -285,11 +285,11 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||
animateReactionsIn = true
|
||||
}
|
||||
|
||||
reactionContextNode.reactionSelected = { [weak self] reaction in
|
||||
reactionContextNode.reactionSelected = { [weak self] reaction, isLarge in
|
||||
guard let strongSelf = self, let controller = strongSelf.getController() as? ContextController else {
|
||||
return
|
||||
}
|
||||
controller.reactionSelected?(reaction)
|
||||
controller.reactionSelected?(reaction, isLarge)
|
||||
}
|
||||
}
|
||||
contentTopInset += 70.0
|
||||
|
|
|
|||
|
|
@ -1,32 +1,45 @@
|
|||
import Foundation
|
||||
|
||||
public struct ChatListEntryMessageTagSummaryKey: Hashable {
|
||||
public var tag: MessageTags
|
||||
public var actionType: PendingMessageActionType
|
||||
|
||||
public init(tag: MessageTags, actionType: PendingMessageActionType) {
|
||||
self.tag = tag
|
||||
self.actionType = actionType
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatListEntryMessageTagSummaryComponent {
|
||||
public let tag: MessageTags
|
||||
public let namespace: MessageId.Namespace
|
||||
|
||||
public init(tag: MessageTags, namespace: MessageId.Namespace) {
|
||||
self.tag = tag
|
||||
public init(namespace: MessageId.Namespace) {
|
||||
self.namespace = namespace
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatListEntryPendingMessageActionsSummaryComponent {
|
||||
public let type: PendingMessageActionType
|
||||
public let namespace: MessageId.Namespace
|
||||
|
||||
public init(type: PendingMessageActionType, namespace: MessageId.Namespace) {
|
||||
self.type = type
|
||||
public init(namespace: MessageId.Namespace) {
|
||||
self.namespace = namespace
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatListEntrySummaryComponents {
|
||||
public let tagSummary: ChatListEntryMessageTagSummaryComponent?
|
||||
public let actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent?
|
||||
public struct Component {
|
||||
public let tagSummary: ChatListEntryMessageTagSummaryComponent?
|
||||
public let actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent?
|
||||
|
||||
public init(tagSummary: ChatListEntryMessageTagSummaryComponent? = nil, actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent? = nil) {
|
||||
self.tagSummary = tagSummary
|
||||
self.actionsSummary = actionsSummary
|
||||
}
|
||||
}
|
||||
public var components: [ChatListEntryMessageTagSummaryKey: Component]
|
||||
|
||||
public init(tagSummary: ChatListEntryMessageTagSummaryComponent? = nil, actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent? = nil) {
|
||||
self.tagSummary = tagSummary
|
||||
self.actionsSummary = actionsSummary
|
||||
public init(components: [ChatListEntryMessageTagSummaryKey: Component] = [:]) {
|
||||
self.components = components
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +101,7 @@ public struct ChatListGroupReferenceEntry: Equatable {
|
|||
}
|
||||
|
||||
public enum ChatListEntry: Comparable {
|
||||
case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool)
|
||||
case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], hasFailed: Bool, isContact: Bool)
|
||||
case HoleEntry(ChatListHole)
|
||||
|
||||
public var index: ChatListIndex {
|
||||
|
|
@ -185,7 +198,7 @@ public enum ChatListEntry: Comparable {
|
|||
|
||||
enum MutableChatListEntry: Equatable {
|
||||
case IntermediateMessageEntry(index: ChatListIndex, messageIndex: MessageIndex?)
|
||||
case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool)
|
||||
case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], hasFailedMessages: Bool, isContact: Bool)
|
||||
case HoleEntry(ChatListHole)
|
||||
|
||||
init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) {
|
||||
|
|
@ -588,10 +601,7 @@ final class MutableChatListView {
|
|||
}
|
||||
}
|
||||
|
||||
let tagSummaryCount: Int32? = nil
|
||||
let actionsSummaryCount: Int32? = nil
|
||||
|
||||
return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact)
|
||||
return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: [:], hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -843,26 +843,36 @@ private final class ChatListViewSpaceState {
|
|||
if self.orderedEntries.mutableScan({ entry in
|
||||
switch entry {
|
||||
case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, presence, tagSummaryInfo, hasFailedMessages, isContact):
|
||||
var updatedTagSummaryCount: Int32?
|
||||
var updatedActionsSummaryCount: Int32?
|
||||
var updatedChatListMessageTagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] = tagSummaryInfo
|
||||
var didUpdateSummaryInfo = false
|
||||
|
||||
if let tagSummary = self.summaryComponents.tagSummary {
|
||||
let key = MessageHistoryTagsSummaryKey(tag: tagSummary.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
|
||||
if let summary = transaction.currentUpdatedMessageTagSummaries[key] {
|
||||
updatedTagSummaryCount = summary.count
|
||||
for (key, component) in self.summaryComponents.components {
|
||||
var updatedTagSummaryCount: Int32?
|
||||
if let tagSummary = component.tagSummary {
|
||||
let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
|
||||
if let summary = transaction.currentUpdatedMessageTagSummaries[key] {
|
||||
updatedTagSummaryCount = summary.count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let actionsSummary = self.summaryComponents.actionsSummary {
|
||||
let key = PendingMessageActionsSummaryKey(type: actionsSummary.type, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
|
||||
if let count = transaction.currentUpdatedMessageActionsSummaries[key] {
|
||||
updatedActionsSummaryCount = count
|
||||
}
|
||||
}
|
||||
|
||||
if updatedTagSummaryCount != nil || updatedActionsSummaryCount != nil {
|
||||
let summaryInfo = ChatListMessageTagSummaryInfo(tagSummaryCount: updatedTagSummaryCount ?? tagSummaryInfo.tagSummaryCount, actionsSummaryCount: updatedActionsSummaryCount ?? tagSummaryInfo.actionsSummaryCount)
|
||||
|
||||
var updatedActionsSummaryCount: Int32?
|
||||
if let actionsSummary = component.actionsSummary {
|
||||
let key = PendingMessageActionsSummaryKey(type: key.actionType, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
|
||||
if let count = transaction.currentUpdatedMessageActionsSummaries[key] {
|
||||
updatedActionsSummaryCount = count
|
||||
}
|
||||
}
|
||||
|
||||
if updatedTagSummaryCount != nil || updatedActionsSummaryCount != nil {
|
||||
updatedChatListMessageTagSummaryInfo[key] = ChatListMessageTagSummaryInfo(
|
||||
tagSummaryCount: updatedTagSummaryCount ?? updatedChatListMessageTagSummaryInfo[key]?.tagSummaryCount,
|
||||
actionsSummaryCount: updatedActionsSummaryCount ?? updatedChatListMessageTagSummaryInfo[key]?.actionsSummaryCount
|
||||
)
|
||||
didUpdateSummaryInfo = true
|
||||
}
|
||||
}
|
||||
|
||||
if didUpdateSummaryInfo {
|
||||
return .MessageEntry(
|
||||
index: index,
|
||||
messages: messages,
|
||||
|
|
@ -872,7 +882,7 @@ private final class ChatListViewSpaceState {
|
|||
embeddedInterfaceState: embeddedInterfaceState,
|
||||
renderedPeer: entryRenderedPeer,
|
||||
presence: presence,
|
||||
tagSummaryInfo: summaryInfo,
|
||||
tagSummaryInfo: updatedChatListMessageTagSummaryInfo,
|
||||
hasFailedMessages: hasFailedMessages,
|
||||
isContact: isContact
|
||||
)
|
||||
|
|
@ -1408,23 +1418,29 @@ struct ChatListViewState {
|
|||
}
|
||||
let renderedPeer = RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers)
|
||||
|
||||
var tagSummaryCount: Int32?
|
||||
var actionsSummaryCount: Int32?
|
||||
|
||||
if let tagSummary = self.summaryComponents.tagSummary {
|
||||
let key = MessageHistoryTagsSummaryKey(tag: tagSummary.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
|
||||
if let summary = postbox.messageHistoryTagsSummaryTable.get(key) {
|
||||
tagSummaryCount = summary.count
|
||||
var tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] = [:]
|
||||
for (key, component) in self.summaryComponents.components {
|
||||
var tagSummaryCount: Int32?
|
||||
var actionsSummaryCount: Int32?
|
||||
|
||||
if let tagSummary = component.tagSummary {
|
||||
let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
|
||||
if let summary = postbox.messageHistoryTagsSummaryTable.get(key) {
|
||||
tagSummaryCount = summary.count
|
||||
}
|
||||
}
|
||||
|
||||
if let actionsSummary = component.actionsSummary {
|
||||
let key = PendingMessageActionsSummaryKey(type: key.actionType, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
|
||||
actionsSummaryCount = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(key.peerId, key.namespace, key.type))
|
||||
}
|
||||
|
||||
tagSummaryInfo[key] = ChatListMessageTagSummaryInfo(
|
||||
tagSummaryCount: tagSummaryCount,
|
||||
actionsSummaryCount: actionsSummaryCount
|
||||
)
|
||||
}
|
||||
|
||||
if let actionsSummary = self.summaryComponents.actionsSummary {
|
||||
let key = PendingMessageActionsSummaryKey(type: actionsSummary.type, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
|
||||
actionsSummaryCount = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(key.peerId, key.namespace, key.type))
|
||||
}
|
||||
|
||||
let tagSummaryInfo = ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount)
|
||||
|
||||
let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId)
|
||||
|
||||
let isRemovedFromTotalUnreadCount: Bool
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ final class MessageHistoryTable: Table {
|
|||
|
||||
if (currentTags & 1) != 0 {
|
||||
let tag = MessageTags(rawValue: 1 << UInt32(i))
|
||||
self.tagsTable.add(tags: tag, index: message.index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
|
||||
self.tagsTable.add(tags: tag, index: message.index, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1481,11 +1481,12 @@ final class MessageHistoryTable: Table {
|
|||
let updatedGroupInfo = self.updateMovingGroupInfoInNamespace(index: updatedIndex, updatedIndex: updatedIndex, groupingKey: message.groupingKey, previousInfo: previousMessage.groupInfo, updatedGroupInfos: &updatedGroupInfos)
|
||||
|
||||
if previousMessage.tags != message.tags || index != updatedIndex {
|
||||
let isNewlyAdded = previousMessage.tags.isEmpty
|
||||
if !previousMessage.tags.isEmpty {
|
||||
self.tagsTable.remove(tags: previousMessage.tags, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
|
||||
}
|
||||
if !message.tags.isEmpty {
|
||||
self.tagsTable.add(tags: message.tags, index: message.index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
|
||||
self.tagsTable.add(tags: message.tags, index: message.index, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
|
||||
}
|
||||
}
|
||||
if previousMessage.threadId != message.threadId || index != message.index {
|
||||
|
|
|
|||
|
|
@ -116,9 +116,9 @@ class MessageHistoryTagsSummaryTable: Table {
|
|||
}
|
||||
}
|
||||
|
||||
func addMessage(key: MessageHistoryTagsSummaryKey, id: MessageId.Id, updatedSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) {
|
||||
func addMessage(key: MessageHistoryTagsSummaryKey, id: MessageId.Id, isNewlyAdded: Bool, updatedSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) {
|
||||
if let current = self.get(key) {
|
||||
if !current.range.contains(id) {
|
||||
if !isNewlyAdded || !current.range.contains(id) {
|
||||
self.set(key, summary: current.withAddedCount(1), updatedSummaries: &updatedSummaries)
|
||||
if current.range.maxId == 0 {
|
||||
self.invalidateTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: key.peerId, namespace: key.namespace, tagMask: key.tag), operations: &invalidateSummaries)
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ class MessageHistoryTagsTable: Table {
|
|||
return self.lowerBound(tag: tag, peerId: peerId, namespace: namespace).successor
|
||||
}
|
||||
|
||||
func add(tags: MessageTags, index: MessageIndex, updatedSummaries: inout[MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) {
|
||||
func add(tags: MessageTags, index: MessageIndex, isNewlyAdded: Bool, updatedSummaries: inout[MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) {
|
||||
for tag in tags {
|
||||
self.valueBox.set(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey), value: MemoryBuffer())
|
||||
if self.summaryTags.contains(tag) {
|
||||
self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries)
|
||||
self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, namespace: index.id.namespace), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
private var validLayout: (CGSize, UIEdgeInsets, CGRect)?
|
||||
private var isLeftAligned: Bool = true
|
||||
|
||||
public var reactionSelected: ((ReactionContextItem) -> Void)?
|
||||
public var reactionSelected: ((ReactionContextItem, Bool) -> Void)?
|
||||
|
||||
private var hapticFeedback: HapticFeedback?
|
||||
private var standaloneReactionAnimation: StandaloneReactionAnimation?
|
||||
|
|
@ -721,7 +721,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.longPressTimer?.invalidate()
|
||||
self.continuousHaptic = nil
|
||||
self.didTriggerExpandedReaction = true
|
||||
self.highlightGestureFinished(performAction: true)
|
||||
self.highlightGestureFinished(performAction: true, isLarge: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
@ -732,7 +732,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
case .ended:
|
||||
let point = recognizer.location(in: self.view)
|
||||
if let reaction = self.reaction(at: point) {
|
||||
self.reactionSelected?(reaction)
|
||||
self.reactionSelected?(reaction, false)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
|
@ -755,10 +755,14 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
}
|
||||
|
||||
public func highlightGestureFinished(performAction: Bool) {
|
||||
self.highlightGestureFinished(performAction: performAction, isLarge: false)
|
||||
}
|
||||
|
||||
private func highlightGestureFinished(performAction: Bool, isLarge: Bool) {
|
||||
if let highlightedReaction = self.highlightedReaction {
|
||||
self.highlightedReaction = nil
|
||||
if performAction {
|
||||
self.performReactionSelection(reaction: highlightedReaction)
|
||||
self.performReactionSelection(reaction: highlightedReaction, isLarge: isLarge)
|
||||
} else {
|
||||
if let (size, insets, anchorRect) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||
|
|
@ -819,10 +823,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
return self.reactionItemNode(at: point)?.item
|
||||
}
|
||||
|
||||
public func performReactionSelection(reaction: ReactionContextItem.Reaction) {
|
||||
public func performReactionSelection(reaction: ReactionContextItem.Reaction, isLarge: Bool) {
|
||||
for (_, itemNode) in self.visibleItemNodes {
|
||||
if itemNode.item.reaction == reaction {
|
||||
self.reactionSelected?(itemNode.item)
|
||||
self.reactionSelected?(itemNode.item, isLarge)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
|||
EnginePeer.Presence(status: .present(until: presenceTimestamp + 1000), lastActivity: presenceTimestamp)
|
||||
},
|
||||
hasUnseenMentions: false,
|
||||
hasUnseenReactions: false,
|
||||
draftState: nil,
|
||||
inputActivities: hasInputActivity ? [(author, .typingText)] : [],
|
||||
promoInfo: nil,
|
||||
|
|
|
|||
|
|
@ -886,6 +886,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||
EnginePeer.Presence(status: .present(until: presenceTimestamp + 1000), lastActivity: presenceTimestamp)
|
||||
},
|
||||
hasUnseenMentions: false,
|
||||
hasUnseenReactions: false,
|
||||
draftState: nil,
|
||||
inputActivities: hasInputActivity ? [(author, .typingText)] : [],
|
||||
promoInfo: nil,
|
||||
|
|
|
|||
|
|
@ -409,6 +409,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
EnginePeer.Presence(status: .present(until: presenceTimestamp + 1000), lastActivity: presenceTimestamp)
|
||||
},
|
||||
hasUnseenMentions: false,
|
||||
hasUnseenReactions: false,
|
||||
draftState: nil,
|
||||
inputActivities: hasInputActivity ? [(author, .typingText)] : [],
|
||||
promoInfo: nil,
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[2011940674] = { return Api.UserStatus.parse_userStatusLastMonth($0) }
|
||||
dict[-911191137] = { return Api.SearchResultsCalendarPeriod.parse_searchResultsCalendarPeriod($0) }
|
||||
dict[-11252123] = { return Api.Folder.parse_folder($0) }
|
||||
dict[739712882] = { return Api.Dialog.parse_dialog($0) }
|
||||
dict[-1460809483] = { return Api.Dialog.parse_dialog($0) }
|
||||
dict[1908216652] = { return Api.Dialog.parse_dialogFolder($0) }
|
||||
dict[381645902] = { return Api.SendMessageAction.parse_sendMessageTypingAction($0) }
|
||||
dict[-44119819] = { return Api.SendMessageAction.parse_sendMessageCancelAction($0) }
|
||||
|
|
@ -342,7 +342,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-842824308] = { return Api.account.WallPapers.parse_wallPapers($0) }
|
||||
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
||||
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
||||
dict[142306870] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||
dict[1328256121] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
|
||||
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
|
||||
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
|
||||
|
|
@ -518,7 +518,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) }
|
||||
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
|
||||
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
|
||||
dict[-1553558980] = { return Api.messages.MessageReactionsList.parse_messageReactionsList($0) }
|
||||
dict[834488621] = { return Api.messages.MessageReactionsList.parse_messageReactionsList($0) }
|
||||
dict[-1014526429] = { return Api.help.Country.parse_country($0) }
|
||||
dict[-1660637285] = { return Api.StatsGroupTopPoster.parse_statsGroupTopPoster($0) }
|
||||
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
|
||||
|
|
@ -793,8 +793,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) }
|
||||
dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) }
|
||||
dict[-1065882623] = { return Api.AvailableReaction.parse_availableReaction($0) }
|
||||
dict[1741309751] = { return Api.messages.TranslatedText.parse_translateNoResult($0) }
|
||||
dict[-1575684144] = { return Api.messages.TranslatedText.parse_translateResultText($0) }
|
||||
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
||||
dict[-1826077446] = { return Api.MessageUserReaction.parse_messageUserReaction($0) }
|
||||
dict[1370914559] = { return Api.MessagePeerReaction.parse_messagePeerReaction($0) }
|
||||
dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) }
|
||||
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
|
||||
dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
|
||||
|
|
@ -1574,9 +1576,11 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.AvailableReaction:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.TranslatedText:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.InviteText:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageUserReaction:
|
||||
case let _1 as Api.MessagePeerReaction:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BotInlineMessage:
|
||||
_1.serialize(buffer, boxed)
|
||||
|
|
|
|||
|
|
@ -1219,13 +1219,13 @@ public struct messages {
|
|||
|
||||
}
|
||||
public enum MessageReactionsList: TypeConstructorDescription {
|
||||
case messageReactionsList(flags: Int32, count: Int32, reactions: [Api.MessageUserReaction], users: [Api.User], nextOffset: String?)
|
||||
case messageReactionsList(flags: Int32, count: Int32, reactions: [Api.MessagePeerReaction], chats: [Api.Chat], 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):
|
||||
case .messageReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1553558980)
|
||||
buffer.appendInt32(834488621)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
|
|
@ -1235,6 +1235,11 @@ public struct messages {
|
|||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
|
|
@ -1246,8 +1251,8 @@ public struct messages {
|
|||
|
||||
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)])
|
||||
case .messageReactionsList(let flags, let count, let reactions, let chats, let users, let nextOffset):
|
||||
return ("messageReactionsList", [("flags", flags), ("count", count), ("reactions", reactions), ("chats", chats), ("users", users), ("nextOffset", nextOffset)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1256,23 +1261,28 @@ public struct messages {
|
|||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.MessageUserReaction]?
|
||||
var _3: [Api.MessagePeerReaction]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserReaction.self)
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerReaction.self)
|
||||
}
|
||||
var _4: [Api.User]?
|
||||
var _4: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _5: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) }
|
||||
var _5: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = 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)
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.messages.MessageReactionsList.messageReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -2473,6 +2483,52 @@ public struct messages {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum TranslatedText: TypeConstructorDescription {
|
||||
case translateNoResult
|
||||
case translateResultText(text: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .translateNoResult:
|
||||
if boxed {
|
||||
buffer.appendInt32(1741309751)
|
||||
}
|
||||
|
||||
break
|
||||
case .translateResultText(let text):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1575684144)
|
||||
}
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .translateNoResult:
|
||||
return ("translateNoResult", [])
|
||||
case .translateResultText(let text):
|
||||
return ("translateResultText", [("text", text)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_translateNoResult(_ reader: BufferReader) -> TranslatedText? {
|
||||
return Api.messages.TranslatedText.translateNoResult
|
||||
}
|
||||
public static func parse_translateResultText(_ reader: BufferReader) -> TranslatedText? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.messages.TranslatedText.translateResultText(text: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum FavedStickers: TypeConstructorDescription {
|
||||
case favedStickersNotModified
|
||||
|
|
|
|||
|
|
@ -4236,14 +4236,14 @@ public extension Api {
|
|||
|
||||
}
|
||||
public enum Dialog: TypeConstructorDescription {
|
||||
case dialog(flags: Int32, peer: Api.Peer, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, notifySettings: Api.PeerNotifySettings, pts: Int32?, draft: Api.DraftMessage?, folderId: Int32?)
|
||||
case dialog(flags: Int32, peer: Api.Peer, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32, notifySettings: Api.PeerNotifySettings, pts: Int32?, draft: Api.DraftMessage?, folderId: Int32?)
|
||||
case dialogFolder(flags: Int32, folder: Api.Folder, peer: Api.Peer, topMessage: Int32, unreadMutedPeersCount: Int32, unreadUnmutedPeersCount: Int32, unreadMutedMessagesCount: Int32, unreadUnmutedMessagesCount: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let notifySettings, let pts, let draft, let folderId):
|
||||
case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount, let notifySettings, let pts, let draft, let folderId):
|
||||
if boxed {
|
||||
buffer.appendInt32(739712882)
|
||||
buffer.appendInt32(-1460809483)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
|
|
@ -4252,6 +4252,7 @@ public extension Api {
|
|||
serializeInt32(readOutboxMaxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(unreadCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(unreadMentionsCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(unreadReactionsCount, buffer: buffer, boxed: false)
|
||||
notifySettings.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(pts!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {draft!.serialize(buffer, true)}
|
||||
|
|
@ -4275,8 +4276,8 @@ public extension Api {
|
|||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let notifySettings, let pts, let draft, let folderId):
|
||||
return ("dialog", [("flags", flags), ("peer", peer), ("topMessage", topMessage), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("unreadMentionsCount", unreadMentionsCount), ("notifySettings", notifySettings), ("pts", pts), ("draft", draft), ("folderId", folderId)])
|
||||
case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount, let notifySettings, let pts, let draft, let folderId):
|
||||
return ("dialog", [("flags", flags), ("peer", peer), ("topMessage", topMessage), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("unreadMentionsCount", unreadMentionsCount), ("unreadReactionsCount", unreadReactionsCount), ("notifySettings", notifySettings), ("pts", pts), ("draft", draft), ("folderId", folderId)])
|
||||
case .dialogFolder(let flags, let folder, let peer, let topMessage, let unreadMutedPeersCount, let unreadUnmutedPeersCount, let unreadMutedMessagesCount, let unreadUnmutedMessagesCount):
|
||||
return ("dialogFolder", [("flags", flags), ("folder", folder), ("peer", peer), ("topMessage", topMessage), ("unreadMutedPeersCount", unreadMutedPeersCount), ("unreadUnmutedPeersCount", unreadUnmutedPeersCount), ("unreadMutedMessagesCount", unreadMutedMessagesCount), ("unreadUnmutedMessagesCount", unreadUnmutedMessagesCount)])
|
||||
}
|
||||
|
|
@ -4299,18 +4300,20 @@ public extension Api {
|
|||
_6 = reader.readInt32()
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
var _8: Api.PeerNotifySettings?
|
||||
var _8: Int32?
|
||||
_8 = reader.readInt32()
|
||||
var _9: Api.PeerNotifySettings?
|
||||
if let signature = reader.readInt32() {
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
|
||||
}
|
||||
var _9: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_9 = reader.readInt32() }
|
||||
var _10: Api.DraftMessage?
|
||||
var _10: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_10 = reader.readInt32() }
|
||||
var _11: Api.DraftMessage?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_10 = Api.parse(reader, signature: signature) as? Api.DraftMessage
|
||||
_11 = Api.parse(reader, signature: signature) as? Api.DraftMessage
|
||||
} }
|
||||
var _11: Int32?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_11 = reader.readInt32() }
|
||||
var _12: Int32?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_12 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -4319,11 +4322,12 @@ public extension Api {
|
|||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.Dialog.dialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, unreadMentionsCount: _7!, notifySettings: _8!, pts: _9, draft: _10, folderId: _11)
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 0) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 1) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 4) == 0) || _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.Dialog.dialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, unreadMentionsCount: _7!, unreadReactionsCount: _8!, notifySettings: _9!, pts: _10, draft: _11, folderId: _12)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -8922,13 +8926,13 @@ public extension Api {
|
|||
|
||||
}
|
||||
public enum MessageReactions: TypeConstructorDescription {
|
||||
case messageReactions(flags: Int32, results: [Api.ReactionCount], recentReactons: [Api.MessageUserReaction]?)
|
||||
case messageReactions(flags: Int32, results: [Api.ReactionCount], recentReactions: [Api.MessagePeerReaction]?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageReactions(let flags, let results, let recentReactons):
|
||||
case .messageReactions(let flags, let results, let recentReactions):
|
||||
if boxed {
|
||||
buffer.appendInt32(142306870)
|
||||
buffer.appendInt32(1328256121)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
|
|
@ -8937,8 +8941,8 @@ public extension Api {
|
|||
item.serialize(buffer, true)
|
||||
}
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(recentReactons!.count))
|
||||
for item in recentReactons! {
|
||||
buffer.appendInt32(Int32(recentReactions!.count))
|
||||
for item in recentReactions! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
break
|
||||
|
|
@ -8947,8 +8951,8 @@ public extension Api {
|
|||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageReactions(let flags, let results, let recentReactons):
|
||||
return ("messageReactions", [("flags", flags), ("results", results), ("recentReactons", recentReactons)])
|
||||
case .messageReactions(let flags, let results, let recentReactions):
|
||||
return ("messageReactions", [("flags", flags), ("results", results), ("recentReactions", recentReactions)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8959,15 +8963,15 @@ public extension Api {
|
|||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self)
|
||||
}
|
||||
var _3: [Api.MessageUserReaction]?
|
||||
var _3: [Api.MessagePeerReaction]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserReaction.self)
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerReaction.self)
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.MessageReactions.messageReactions(flags: _1!, results: _2!, recentReactons: _3)
|
||||
return Api.MessageReactions.messageReactions(flags: _1!, results: _2!, recentReactions: _3)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -20319,16 +20323,17 @@ public extension Api {
|
|||
}
|
||||
|
||||
}
|
||||
public enum MessageUserReaction: TypeConstructorDescription {
|
||||
case messageUserReaction(userId: Int64, reaction: String)
|
||||
public enum MessagePeerReaction: TypeConstructorDescription {
|
||||
case messagePeerReaction(flags: Int32, peerId: Api.Peer, reaction: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageUserReaction(let userId, let reaction):
|
||||
case .messagePeerReaction(let flags, let peerId, let reaction):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1826077446)
|
||||
buffer.appendInt32(1370914559)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peerId.serialize(buffer, true)
|
||||
serializeString(reaction, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
|
|
@ -20336,20 +20341,25 @@ public extension Api {
|
|||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageUserReaction(let userId, let reaction):
|
||||
return ("messageUserReaction", [("userId", userId), ("reaction", reaction)])
|
||||
case .messagePeerReaction(let flags, let peerId, let reaction):
|
||||
return ("messagePeerReaction", [("flags", flags), ("peerId", peerId), ("reaction", reaction)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageUserReaction(_ reader: BufferReader) -> MessageUserReaction? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
public static func parse_messagePeerReaction(_ reader: BufferReader) -> MessagePeerReaction? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageUserReaction.messageUserReaction(userId: _1!, reaction: _2!)
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.MessagePeerReaction.messagePeerReaction(flags: _1!, peerId: _2!, reaction: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -4605,6 +4605,58 @@ public extension Api {
|
|||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func translateText(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, text: String?, fromLang: String?, toLang: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.TranslatedText>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(617508334)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(msgId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(text!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(fromLang!, buffer: buffer, boxed: false)}
|
||||
serializeString(toLang, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.translateText", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("text", text), ("fromLang", fromLang), ("toLang", toLang)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.TranslatedText? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.TranslatedText?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.TranslatedText
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getUnreadReactions(peer: Api.InputPeer, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-396644838)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(offsetId, buffer: buffer, boxed: false)
|
||||
serializeInt32(addOffset, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
serializeInt32(maxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(minId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.getUnreadReactions", parameters: [("peer", peer), ("offsetId", offsetId), ("addOffset", addOffset), ("limit", limit), ("maxId", maxId), ("minId", minId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.Messages?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.Messages
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func readReactions(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.AffectedHistory>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2099097129)
|
||||
peer.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.readReactions", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.AffectedHistory?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.AffectedHistory
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct channels {
|
||||
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
|
|
|
|||
|
|
@ -1084,6 +1084,7 @@ public class Account {
|
|||
self.managedOperationsDisposable.add(managedLocalTypingActivities(activities: self.localInputActivityManager.allActivities(), postbox: self.postbox, network: self.network, accountPeerId: self.peerId).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedConsumePersonalMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedReadReactionActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.postbox, network: self.network).start())
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ enum AccountStateMutationOperation {
|
|||
case ResetReadState(peerId: PeerId, namespace: MessageId.Namespace, maxIncomingReadId: MessageId.Id, maxOutgoingReadId: MessageId.Id, maxKnownId: MessageId.Id, count: Int32, markedUnread: Bool?)
|
||||
case ResetIncomingReadState(groupId: PeerGroupId, peerId: PeerId, namespace: MessageId.Namespace, maxIncomingReadId: MessageId.Id, count: Int32, pts: Int32)
|
||||
case UpdatePeerChatUnreadMark(PeerId, MessageId.Namespace, Bool)
|
||||
case ResetMessageTagSummary(PeerId, MessageId.Namespace, Int32, MessageHistoryTagNamespaceCountValidityRange)
|
||||
case ResetMessageTagSummary(PeerId, MessageTags, MessageId.Namespace, Int32, MessageHistoryTagNamespaceCountValidityRange)
|
||||
case ReadGroupFeedInbox(PeerGroupId, MessageIndex)
|
||||
case UpdateState(AuthorizedAccountState.State)
|
||||
case UpdateChannelState(PeerId, Int32)
|
||||
|
|
@ -306,8 +306,8 @@ struct AccountMutableState {
|
|||
self.addOperation(.UpdatePeerChatUnreadMark(peerId, namespace, value))
|
||||
}
|
||||
|
||||
mutating func resetMessageTagSummary(_ peerId: PeerId, namespace: MessageId.Namespace, count: Int32, range: MessageHistoryTagNamespaceCountValidityRange) {
|
||||
self.addOperation(.ResetMessageTagSummary(peerId, namespace, count, range))
|
||||
mutating func resetMessageTagSummary(_ peerId: PeerId, tag: MessageTags, namespace: MessageId.Namespace, count: Int32, range: MessageHistoryTagNamespaceCountValidityRange) {
|
||||
self.addOperation(.ResetMessageTagSummary(peerId, tag, namespace, count, range))
|
||||
}
|
||||
|
||||
mutating func updateState(_ state: AuthorizedAccountState.State) {
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ private var declaredEncodables: Void = {
|
|||
declareEncodable(TelegramMediaExpiredContent.self, f: { TelegramMediaExpiredContent(decoder: $0) })
|
||||
declareEncodable(ConsumablePersonalMentionMessageAttribute.self, f: { ConsumablePersonalMentionMessageAttribute(decoder: $0) })
|
||||
declareEncodable(ConsumePersonalMessageAction.self, f: { ConsumePersonalMessageAction(decoder: $0) })
|
||||
declareEncodable(ReadReactionAction.self, f: { ReadReactionAction(decoder: $0) })
|
||||
declareEncodable(SynchronizeGroupedPeersOperation.self, f: { SynchronizeGroupedPeersOperation(decoder: $0) })
|
||||
declareEncodable(TelegramDeviceContactImportedData.self, f: { TelegramDeviceContactImportedData(decoder: $0) })
|
||||
declareEncodable(SecureFileMediaResource.self, f: { SecureFileMediaResource(decoder: $0) })
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ extension ReactionsMessageAttribute {
|
|||
if let recentReactions = recentReactions {
|
||||
parsedRecentReactions = recentReactions.map { recentReaction -> ReactionsMessageAttribute.RecentPeer in
|
||||
switch recentReaction {
|
||||
case let .messageUserReaction(userId, reaction):
|
||||
return ReactionsMessageAttribute.RecentPeer(value: reaction, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)))
|
||||
case let .messagePeerReaction(flags, peerId, reaction):
|
||||
let isLarge = (flags & (1 << 0)) != 0
|
||||
let isUnseen = (flags & (1 << 1)) != 0
|
||||
return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, peerId: peerId.peerId)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -103,7 +105,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM
|
|||
}
|
||||
}
|
||||
if let value = pending.value {
|
||||
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: value, peerId: accountPeerId))
|
||||
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: value, isLarge: false, isUnseen: false, peerId: accountPeerId))
|
||||
}
|
||||
}
|
||||
for i in (0 ..< reactions.count).reversed() {
|
||||
|
|
@ -137,8 +139,10 @@ extension ReactionsMessageAttribute {
|
|||
if let recentReactions = recentReactions {
|
||||
parsedRecentReactions = recentReactions.map { recentReaction -> ReactionsMessageAttribute.RecentPeer in
|
||||
switch recentReaction {
|
||||
case let .messageUserReaction(userId, reaction):
|
||||
return ReactionsMessageAttribute.RecentPeer(value: reaction, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)))
|
||||
case let .messagePeerReaction(flags, peerId, reaction):
|
||||
let isLarge = (flags & (1 << 0)) != 0
|
||||
let isUnseen = (flags & (1 << 1)) != 0
|
||||
return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, peerId: peerId.peerId)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import TelegramApi
|
|||
public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], media: [Media], textEntities: [MessageTextEntity]?, isPinned: Bool) -> (MessageTags, GlobalMessageTags) {
|
||||
var isSecret = false
|
||||
var isUnconsumedPersonalMention = false
|
||||
var hasUnseenReactions = false
|
||||
for attribute in attributes {
|
||||
if let timerAttribute = attribute as? AutoclearTimeoutMessageAttribute {
|
||||
if timerAttribute.timeout > 0 && timerAttribute.timeout <= 60 {
|
||||
|
|
@ -19,6 +20,8 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
|||
if !mentionAttribute.consumed {
|
||||
isUnconsumedPersonalMention = true
|
||||
}
|
||||
} else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen {
|
||||
hasUnseenReactions = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +31,9 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
|||
if isUnconsumedPersonalMention {
|
||||
tags.insert(.unseenPersonalMessage)
|
||||
}
|
||||
if hasUnseenReactions {
|
||||
tags.insert(.unseenReaction)
|
||||
}
|
||||
|
||||
if isPinned {
|
||||
tags.insert(.pinned)
|
||||
|
|
|
|||
|
|
@ -1646,7 +1646,7 @@ private func resolveMissingPeerChatInfos(network: Network, state: AccountMutable
|
|||
|
||||
for dialog in dialogs {
|
||||
switch dialog {
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, _, folderId):
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, notifySettings, pts, _, folderId):
|
||||
let peerId = peer.peerId
|
||||
|
||||
updatedState.setNeedsHoleFromPreviousState(peerId: peerId, namespace: Namespaces.Message.Cloud, validateChannelPts: pts)
|
||||
|
|
@ -1691,7 +1691,8 @@ private func resolveMissingPeerChatInfos(network: Network, state: AccountMutable
|
|||
updatedState.updateNotificationSettings(.peer(peer.peerId), notificationSettings: notificationSettings)
|
||||
|
||||
updatedState.resetReadState(peer.peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: nil)
|
||||
updatedState.resetMessageTagSummary(peer.peerId, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage))
|
||||
updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage))
|
||||
updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage))
|
||||
updatedState.peerChatInfos[peer.peerId] = PeerChatInfo(notificationSettings: notificationSettings)
|
||||
if let pts = pts {
|
||||
channelStates[peer.peerId] = ChannelState(pts: pts, invalidatedPts: pts, synchronizedUntilMessageId: nil)
|
||||
|
|
@ -1860,7 +1861,7 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl
|
|||
|
||||
var storeMessages: [StoreMessage] = []
|
||||
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
|
||||
var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]
|
||||
var mentionTagSummaries: [PeerId: (tag: MessageTags, summary: MessageHistoryTagNamespaceSummary)] = [:]
|
||||
var channelStates: [PeerId: AccountStateChannelState] = [:]
|
||||
var invalidateChannelStates: [PeerId: Int32] = [:]
|
||||
var channelSynchronizedUntilMessage: [PeerId: MessageId.Id] = [:]
|
||||
|
|
@ -1879,12 +1880,13 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl
|
|||
let apiTopMessage: Int32
|
||||
let apiUnreadCount: Int32
|
||||
let apiUnreadMentionsCount: Int32
|
||||
let apiUnreadReactionsCount: Int32
|
||||
var apiChannelPts: Int32?
|
||||
let apiNotificationSettings: Api.PeerNotifySettings
|
||||
let apiMarkedUnread: Bool
|
||||
let groupId: PeerGroupId
|
||||
switch dialog {
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _, folderId):
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, peerNotificationSettings, pts, _, folderId):
|
||||
apiPeer = peer
|
||||
apiTopMessage = topMessage
|
||||
apiReadInboxMaxId = readInboxMaxId
|
||||
|
|
@ -1892,6 +1894,7 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl
|
|||
apiUnreadCount = unreadCount
|
||||
apiMarkedUnread = (flags & (1 << 3)) != 0
|
||||
apiUnreadMentionsCount = unreadMentionsCount
|
||||
apiUnreadReactionsCount = unreadReactionsCount
|
||||
apiNotificationSettings = peerNotificationSettings
|
||||
apiChannelPts = pts
|
||||
groupId = PeerGroupId(rawValue: folderId ?? 0)
|
||||
|
|
@ -1908,7 +1911,8 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl
|
|||
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount, markedUnread: apiMarkedUnread)
|
||||
|
||||
if apiTopMessage != 0 {
|
||||
mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage))
|
||||
mentionTagSummaries[peerId] = (MessageTags.unseenPersonalMessage, MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)))
|
||||
mentionTagSummaries[peerId] = (MessageTags.unseenReaction, MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadReactionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)))
|
||||
}
|
||||
|
||||
if let apiChannelPts = apiChannelPts {
|
||||
|
|
@ -1966,7 +1970,7 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl
|
|||
}
|
||||
|
||||
for (peerId, tagSummary) in mentionTagSummaries {
|
||||
updatedState.resetMessageTagSummary(peerId, namespace: Namespaces.Message.Cloud, count: tagSummary.count, range: tagSummary.range)
|
||||
updatedState.resetMessageTagSummary(peerId, tag: tagSummary.tag, namespace: Namespaces.Message.Cloud, count: tagSummary.summary.count, range: tagSummary.summary.range)
|
||||
}
|
||||
|
||||
for (peerId, channelState) in channelStates {
|
||||
|
|
@ -2116,18 +2120,18 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
|
|||
case let .channelDifferenceTooLong(_, timeout, dialog, messages, chats, users):
|
||||
apiTimeout = timeout
|
||||
|
||||
var parameters: (peer: Api.Peer, pts: Int32, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32)?
|
||||
var parameters: (peer: Api.Peer, pts: Int32, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32)?
|
||||
|
||||
switch dialog {
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, _, pts, _, _):
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, _, pts, _, _):
|
||||
if let pts = pts {
|
||||
parameters = (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount)
|
||||
parameters = (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount)
|
||||
}
|
||||
case .dialogFolder:
|
||||
break
|
||||
}
|
||||
|
||||
if let (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount) = parameters {
|
||||
if let (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount) = parameters {
|
||||
updatedState.updateChannelState(peer.peerId, pts: pts)
|
||||
updatedState.updateChannelInvalidationPts(peer.peerId, invalidationPts: pts)
|
||||
|
||||
|
|
@ -2161,7 +2165,8 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
|
|||
|
||||
updatedState.resetReadState(peer.peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: nil)
|
||||
|
||||
updatedState.resetMessageTagSummary(peer.peerId, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage))
|
||||
updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage))
|
||||
updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage))
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
|
|
@ -2845,14 +2850,16 @@ func replayFinalState(
|
|||
}
|
||||
case let .UpdatePeerChatUnreadMark(peerId, namespace, value):
|
||||
transaction.applyMarkUnread(peerId: peerId, namespace: namespace, value: value, interactive: false)
|
||||
case let .ResetMessageTagSummary(peerId, namespace, count, range):
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: namespace, count: count, maxId: range.maxId)
|
||||
case let .ResetMessageTagSummary(peerId, tag, namespace, count, range):
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: tag, namespace: namespace, count: count, maxId: range.maxId)
|
||||
if count == 0 {
|
||||
transaction.removeHole(peerId: peerId, namespace: namespace, space: .tag(.unseenPersonalMessage), range: 1 ... (Int32.max - 1))
|
||||
let ids = transaction.getMessageIndicesWithTag(peerId: peerId, namespace: namespace, tag: .unseenPersonalMessage).map({ $0.id })
|
||||
Logger.shared.log("State", "will call markUnseenPersonalMessage for \(ids.count) messages")
|
||||
for id in ids {
|
||||
markUnseenPersonalMessage(transaction: transaction, id: id, addSynchronizeAction: false)
|
||||
transaction.removeHole(peerId: peerId, namespace: namespace, space: .tag(tag), range: 1 ... (Int32.max - 1))
|
||||
if tag == .unseenPersonalMessage {
|
||||
let ids = transaction.getMessageIndicesWithTag(peerId: peerId, namespace: namespace, tag: tag).map({ $0.id })
|
||||
Logger.shared.log("State", "will call markUnseenPersonalMessage for \(ids.count) messages")
|
||||
for id in ids {
|
||||
markUnseenPersonalMessage(transaction: transaction, id: id, addSynchronizeAction: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .UpdateState(innerState):
|
||||
|
|
@ -3288,7 +3295,14 @@ func replayFinalState(
|
|||
}
|
||||
}
|
||||
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
var tags = currentMessage.tags
|
||||
if updatedReactions.hasUnseen {
|
||||
tags.insert(.unseenReaction)
|
||||
} else {
|
||||
tags.remove(.unseenReaction)
|
||||
}
|
||||
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
if let generatedEvent = generatedEvent {
|
||||
addedReactionEvents.append(generatedEvent)
|
||||
|
|
|
|||
|
|
@ -307,6 +307,7 @@ public final class AccountViewTracker {
|
|||
private var updatedUnsupportedMediaDisposables = DisposableDict<Int32>()
|
||||
|
||||
private var updatedSeenPersonalMessageIds = Set<MessageId>()
|
||||
private var updatedReactionsSeenForMessageIds = Set<MessageId>()
|
||||
|
||||
private var cachedDataContexts: [PeerId: PeerCachedDataContext] = [:]
|
||||
private var cachedChannelParticipantsContexts: [PeerId: CachedChannelParticipantsContext] = [:]
|
||||
|
|
@ -1227,6 +1228,36 @@ public final class AccountViewTracker {
|
|||
}
|
||||
}
|
||||
|
||||
public func updateMarkReactionsSeenForMessageIds(messageIds: Set<MessageId>) {
|
||||
self.queue.async {
|
||||
let addedMessageIds: [MessageId] = Array(messageIds)
|
||||
if !addedMessageIds.isEmpty {
|
||||
if let account = self.account {
|
||||
let _ = (account.postbox.transaction { transaction -> Void in
|
||||
for id in addedMessageIds {
|
||||
if let _ = transaction.getMessage(id) {
|
||||
/*transaction.updateMessage(id, update: { currentMessage in
|
||||
var attributes = currentMessage.attributes
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute {
|
||||
attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: attribute.consumed, pending: true)
|
||||
break loop
|
||||
}
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})*/
|
||||
|
||||
if transaction.getPendingMessageAction(type: .readReaction, id: id) == nil {
|
||||
transaction.setPendingMessageAction(type: .readReaction, id: id, action: ReadReactionAction())
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func forceUpdateCachedPeerData(peerId: PeerId) {
|
||||
self.queue.async {
|
||||
let context: PeerCachedDataContext
|
||||
|
|
@ -1756,23 +1787,45 @@ public final class AccountViewTracker {
|
|||
}
|
||||
}
|
||||
|
||||
public func unseenPersonalMessagesCount(peerId: PeerId) -> Signal<Int32, NoError> {
|
||||
public func unseenPersonalMessagesAndReactionCount(peerId: PeerId) -> Signal<(mentionCount: Int32, reactionCount: Int32), NoError> {
|
||||
if let account = self.account {
|
||||
let pendingKey: PostboxViewKey = .pendingMessageActionsSummary(type: .consumeUnseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||
let summaryKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||
return account.postbox.combinedView(keys: [pendingKey, summaryKey])
|
||||
|> map { views -> Int32 in
|
||||
var count: Int32 = 0
|
||||
if let view = views.views[pendingKey] as? PendingMessageActionsSummaryView {
|
||||
count -= view.count
|
||||
let pendingMentionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .consumeUnseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||
let summaryMentionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||
|
||||
let pendingReactionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .readReaction, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||
let summaryReactionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenReaction, peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||
|
||||
return account.postbox.combinedView(keys: [pendingMentionsKey, summaryMentionsKey, pendingReactionsKey, summaryReactionsKey])
|
||||
|> map { views -> (mentionCount: Int32, reactionCount: Int32) in
|
||||
var mentionCount: Int32 = 0
|
||||
if let view = views.views[pendingMentionsKey] as? PendingMessageActionsSummaryView {
|
||||
mentionCount -= view.count
|
||||
}
|
||||
if let view = views.views[summaryKey] as? MessageHistoryTagSummaryView {
|
||||
if let view = views.views[summaryMentionsKey] as? MessageHistoryTagSummaryView {
|
||||
if let unseenCount = view.count {
|
||||
count += unseenCount
|
||||
mentionCount += unseenCount
|
||||
}
|
||||
}
|
||||
return max(0, count)
|
||||
} |> distinctUntilChanged
|
||||
var reactionCount: Int32 = 0
|
||||
if let view = views.views[pendingReactionsKey] as? PendingMessageActionsSummaryView {
|
||||
reactionCount -= view.count
|
||||
}
|
||||
if let view = views.views[summaryReactionsKey] as? MessageHistoryTagSummaryView {
|
||||
if let unseenCount = view.count {
|
||||
reactionCount += unseenCount
|
||||
}
|
||||
}
|
||||
return (max(0, mentionCount), max(0, reactionCount))
|
||||
}
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
if lhs.mentionCount != rhs.mentionCount {
|
||||
return false
|
||||
}
|
||||
if lhs.reactionCount != rhs.reactionCount {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|
|
@ -1802,7 +1855,29 @@ public final class AccountViewTracker {
|
|||
|
||||
public func tailChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, count: Int) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
if let account = self.account {
|
||||
return self.wrappedChatListView(signal: account.postbox.tailChatListView(groupId: groupId, filterPredicate: filterPredicate, count: count, summaryComponents: ChatListEntrySummaryComponents(tagSummary: ChatListEntryMessageTagSummaryComponent(tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(type: PendingMessageActionType.consumeUnseenPersonalMessage, namespace: Namespaces.Message.Cloud))))
|
||||
return self.wrappedChatListView(signal: account.postbox.tailChatListView(
|
||||
groupId: groupId,
|
||||
filterPredicate: filterPredicate,
|
||||
count: count,
|
||||
summaryComponents: ChatListEntrySummaryComponents(
|
||||
components: [
|
||||
ChatListEntryMessageTagSummaryKey(
|
||||
tag: .unseenPersonalMessage,
|
||||
actionType: PendingMessageActionType.consumeUnseenPersonalMessage
|
||||
): ChatListEntrySummaryComponents.Component(
|
||||
tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud),
|
||||
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
|
||||
),
|
||||
ChatListEntryMessageTagSummaryKey(
|
||||
tag: .unseenReaction,
|
||||
actionType: PendingMessageActionType.readReaction
|
||||
): ChatListEntrySummaryComponents.Component(
|
||||
tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud),
|
||||
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|
|
@ -1810,7 +1885,30 @@ public final class AccountViewTracker {
|
|||
|
||||
public func aroundChatListView(groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate? = nil, index: ChatListIndex, count: Int) -> Signal<(ChatListView, ViewUpdateType), NoError> {
|
||||
if let account = self.account {
|
||||
return self.wrappedChatListView(signal: account.postbox.aroundChatListView(groupId: groupId, filterPredicate: filterPredicate, index: index, count: count, summaryComponents: ChatListEntrySummaryComponents(tagSummary: ChatListEntryMessageTagSummaryComponent(tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(type: PendingMessageActionType.consumeUnseenPersonalMessage, namespace: Namespaces.Message.Cloud))))
|
||||
return self.wrappedChatListView(signal: account.postbox.aroundChatListView(
|
||||
groupId: groupId,
|
||||
filterPredicate: filterPredicate,
|
||||
index: index,
|
||||
count: count,
|
||||
summaryComponents: ChatListEntrySummaryComponents(
|
||||
components: [
|
||||
ChatListEntryMessageTagSummaryKey(
|
||||
tag: .unseenPersonalMessage,
|
||||
actionType: PendingMessageActionType.consumeUnseenPersonalMessage
|
||||
): ChatListEntrySummaryComponents.Component(
|
||||
tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud),
|
||||
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
|
||||
),
|
||||
ChatListEntryMessageTagSummaryKey(
|
||||
tag: .unseenReaction,
|
||||
actionType: PendingMessageActionType.readReaction
|
||||
): ChatListEntrySummaryComponents.Component(
|
||||
tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud),
|
||||
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
|
||||
)
|
||||
]
|
||||
)
|
||||
))
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ struct ParsedDialogs {
|
|||
let notificationSettings: [PeerId: PeerNotificationSettings]
|
||||
let readStates: [PeerId: [MessageId.Namespace: PeerReadState]]
|
||||
let mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary]
|
||||
let reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary]
|
||||
let channelStates: [PeerId: Int32]
|
||||
let topMessageIds: [PeerId: MessageId]
|
||||
let storeMessages: [StoreMessage]
|
||||
|
|
@ -49,6 +50,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
|||
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
||||
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
|
||||
var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]
|
||||
var reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]
|
||||
var channelStates: [PeerId: Int32] = [:]
|
||||
var topMessageIds: [PeerId: MessageId] = [:]
|
||||
|
||||
|
|
@ -81,10 +83,11 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
|||
let apiUnreadCount: Int32
|
||||
let apiMarkedUnread: Bool
|
||||
let apiUnreadMentionsCount: Int32
|
||||
let apiUnreadReactionsCount: Int32
|
||||
var apiChannelPts: Int32?
|
||||
let apiNotificationSettings: Api.PeerNotifySettings
|
||||
switch dialog {
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _, _):
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, peerNotificationSettings, pts, _, _):
|
||||
if let peer = peers[peer.peerId] {
|
||||
var isExluded = false
|
||||
if let group = peer as? TelegramGroup {
|
||||
|
|
@ -103,6 +106,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
|||
apiUnreadCount = unreadCount
|
||||
apiMarkedUnread = (flags & (1 << 3)) != 0
|
||||
apiUnreadMentionsCount = unreadMentionsCount
|
||||
apiUnreadReactionsCount = unreadReactionsCount
|
||||
apiNotificationSettings = peerNotificationSettings
|
||||
apiChannelPts = pts
|
||||
let isPinned = (flags & (1 << 2)) != 0
|
||||
|
|
@ -126,6 +130,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
|||
|
||||
if apiTopMessage != 0 {
|
||||
mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage))
|
||||
reactionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadReactionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage))
|
||||
topMessageIds[peerId] = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: apiTopMessage)
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +178,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message],
|
|||
notificationSettings: notificationSettings,
|
||||
readStates: readStates,
|
||||
mentionTagSummaries: mentionTagSummaries,
|
||||
reactionTagSummaries: reactionTagSummaries,
|
||||
channelStates: channelStates,
|
||||
topMessageIds: topMessageIds,
|
||||
storeMessages: storeMessages,
|
||||
|
|
@ -189,6 +195,7 @@ struct FetchedChatList {
|
|||
let notificationSettings: [PeerId: PeerNotificationSettings]
|
||||
let readStates: [PeerId: [MessageId.Namespace: PeerReadState]]
|
||||
let mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary]
|
||||
let reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary]
|
||||
let channelStates: [PeerId: Int32]
|
||||
let storeMessages: [StoreMessage]
|
||||
let topMessageIds: [PeerId: MessageId]
|
||||
|
|
@ -294,6 +301,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
|||
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
||||
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
|
||||
var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]
|
||||
var reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:]
|
||||
var channelStates: [PeerId: Int32] = [:]
|
||||
var storeMessages: [StoreMessage] = []
|
||||
var topMessageIds: [PeerId: MessageId] = [:]
|
||||
|
|
@ -303,6 +311,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
|||
notificationSettings.merge(parsedRemoteChats.notificationSettings, uniquingKeysWith: { _, updated in updated })
|
||||
readStates.merge(parsedRemoteChats.readStates, uniquingKeysWith: { _, updated in updated })
|
||||
mentionTagSummaries.merge(parsedRemoteChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated })
|
||||
reactionTagSummaries.merge(parsedRemoteChats.reactionTagSummaries, uniquingKeysWith: { _, updated in updated })
|
||||
channelStates.merge(parsedRemoteChats.channelStates, uniquingKeysWith: { _, updated in updated })
|
||||
storeMessages.append(contentsOf: parsedRemoteChats.storeMessages)
|
||||
topMessageIds.merge(parsedRemoteChats.topMessageIds, uniquingKeysWith: { _, updated in updated })
|
||||
|
|
@ -313,6 +322,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
|||
notificationSettings.merge(parsedPinnedChats.notificationSettings, uniquingKeysWith: { _, updated in updated })
|
||||
readStates.merge(parsedPinnedChats.readStates, uniquingKeysWith: { _, updated in updated })
|
||||
mentionTagSummaries.merge(parsedPinnedChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated })
|
||||
reactionTagSummaries.merge(parsedPinnedChats.reactionTagSummaries, uniquingKeysWith: { _, updated in updated })
|
||||
channelStates.merge(parsedPinnedChats.channelStates, uniquingKeysWith: { _, updated in updated })
|
||||
storeMessages.append(contentsOf: parsedPinnedChats.storeMessages)
|
||||
topMessageIds.merge(parsedPinnedChats.topMessageIds, uniquingKeysWith: { _, updated in updated })
|
||||
|
|
@ -335,6 +345,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
|||
notificationSettings.merge(folderChats.notificationSettings, uniquingKeysWith: { _, updated in updated })
|
||||
readStates.merge(folderChats.readStates, uniquingKeysWith: { _, updated in updated })
|
||||
mentionTagSummaries.merge(folderChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated })
|
||||
reactionTagSummaries.merge(folderChats.reactionTagSummaries, uniquingKeysWith: { _, updated in updated })
|
||||
channelStates.merge(folderChats.channelStates, uniquingKeysWith: { _, updated in updated })
|
||||
storeMessages.append(contentsOf: folderChats.storeMessages)
|
||||
}
|
||||
|
|
@ -368,6 +379,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo
|
|||
notificationSettings: notificationSettings,
|
||||
readStates: readStates,
|
||||
mentionTagSummaries: mentionTagSummaries,
|
||||
reactionTagSummaries: reactionTagSummaries,
|
||||
channelStates: channelStates,
|
||||
storeMessages: storeMessages,
|
||||
topMessageIds: topMessageIds,
|
||||
|
|
|
|||
|
|
@ -341,6 +341,54 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
|||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
} else if tag == .unseenReaction {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
let selectedLimit = count
|
||||
let maxId: Int32
|
||||
let minId: Int32
|
||||
|
||||
switch direction {
|
||||
case let .range(start, end):
|
||||
if start.id <= end.id {
|
||||
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||
addOffset = Int32(-selectedLimit)
|
||||
maxId = end.id
|
||||
minId = start.id - 1
|
||||
|
||||
let rangeStartId = start.id
|
||||
let rangeEndId = min(end.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
addOffset = 0
|
||||
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||
minId = end.id
|
||||
|
||||
let rangeStartId = end.id
|
||||
let rangeEndId = min(start.id, Int32.max - 1)
|
||||
if rangeStartId <= rangeEndId {
|
||||
minMaxRange = rangeStartId ... rangeEndId
|
||||
} else {
|
||||
minMaxRange = rangeStartId ... rangeStartId
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .aroundId(id):
|
||||
offsetId = id.id
|
||||
addOffset = Int32(-selectedLimit / 2)
|
||||
maxId = Int32.max
|
||||
minId = 1
|
||||
|
||||
minMaxRange = 1 ... Int32.max - 1
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getUnreadReactions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
} else if tag == .liveLocation {
|
||||
let selectedLimit = count
|
||||
|
||||
|
|
@ -661,6 +709,9 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId
|
|||
for (peerId, summary) in fetchedChats.mentionTagSummaries {
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId)
|
||||
}
|
||||
for (peerId, summary) in fetchedChats.reactionTagSummaries {
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId)
|
||||
}
|
||||
|
||||
for (groupId, summary) in fetchedChats.folderSummaries {
|
||||
transaction.resetPeerGroupSummary(groupId: groupId, namespace: Namespaces.Message.Cloud, summary: summary)
|
||||
|
|
|
|||
|
|
@ -76,11 +76,11 @@ private final class ManagedConsumePersonalMessagesActionsHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private func withTakenAction(postbox: Postbox, type: PendingMessageActionType, id: MessageId, _ f: @escaping (Transaction, PendingMessageActionsEntry?) -> Signal<Void, NoError>) -> Signal<Void, NoError> {
|
||||
private func withTakenAction<T: PendingMessageActionData>(postbox: Postbox, type: PendingMessageActionType, actionType: T.Type, id: MessageId, _ f: @escaping (Transaction, PendingMessageActionsEntry?) -> Signal<Void, NoError>) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
var result: PendingMessageActionsEntry?
|
||||
|
||||
if let action = transaction.getPendingMessageAction(type: type, id: id) as? ConsumePersonalMessageAction {
|
||||
if let action = transaction.getPendingMessageAction(type: type, id: id) as? T {
|
||||
result = PendingMessageActionsEntry(id: id, action: action)
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ func managedConsumePersonalMessagesActions(postbox: Postbox, network: Network, s
|
|||
}
|
||||
|
||||
for (entry, disposable) in beginOperations {
|
||||
let signal = withTakenAction(postbox: postbox, type: .consumeUnseenPersonalMessage, id: entry.id, { transaction, entry -> Signal<Void, NoError> in
|
||||
let signal = withTakenAction(postbox: postbox, type: .consumeUnseenPersonalMessage, actionType: ConsumePersonalMessageAction.self, id: entry.id, { transaction, entry -> Signal<Void, NoError> in
|
||||
if let entry = entry {
|
||||
if let _ = entry.action as? ConsumePersonalMessageAction {
|
||||
return synchronizeConsumeMessageContents(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, id: entry.id)
|
||||
|
|
@ -151,6 +151,69 @@ func managedConsumePersonalMessagesActions(postbox: Postbox, network: Network, s
|
|||
}
|
||||
}
|
||||
|
||||
func managedReadReactionActions(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
|
||||
return Signal { _ in
|
||||
let helper = Atomic<ManagedConsumePersonalMessagesActionsHelper>(value: ManagedConsumePersonalMessagesActionsHelper())
|
||||
|
||||
let actionsKey = PostboxViewKey.pendingMessageActions(type: .readReaction)
|
||||
let invalidateKey = PostboxViewKey.invalidatedMessageHistoryTagSummaries(tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud)
|
||||
let disposable = postbox.combinedView(keys: [actionsKey, invalidateKey]).start(next: { view in
|
||||
var entries: [PendingMessageActionsEntry] = []
|
||||
var invalidateEntries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
|
||||
if let v = view.views[actionsKey] as? PendingMessageActionsView {
|
||||
entries = v.entries
|
||||
}
|
||||
if let v = view.views[invalidateKey] as? InvalidatedMessageHistoryTagSummariesView {
|
||||
invalidateEntries = v.entries
|
||||
}
|
||||
|
||||
let (disposeOperations, beginOperations, beginValidateOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PendingMessageActionsEntry, MetaDisposable)], beginValidateOperations: [(InvalidatedMessageHistoryTagsSummaryEntry, MetaDisposable)]) in
|
||||
return helper.update(entries: entries, invalidateEntries: invalidateEntries)
|
||||
}
|
||||
|
||||
for disposable in disposeOperations {
|
||||
disposable.dispose()
|
||||
}
|
||||
|
||||
for (entry, disposable) in beginOperations {
|
||||
let signal = withTakenAction(postbox: postbox, type: .readReaction, actionType: ReadReactionAction.self, id: entry.id, { transaction, entry -> Signal<Void, NoError> in
|
||||
if let entry = entry {
|
||||
if let _ = entry.action as? ReadReactionAction {
|
||||
return synchronizeReadMessageReactions(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, id: entry.id)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
return .complete()
|
||||
})
|
||||
|> then(postbox.transaction { transaction -> Void in
|
||||
transaction.setPendingMessageAction(type: .readReaction, id: entry.id, action: nil)
|
||||
})
|
||||
|
||||
disposable.set(signal.start())
|
||||
}
|
||||
|
||||
for (entry, disposable) in beginValidateOperations {
|
||||
let signal = synchronizeUnseenReactionsTag(postbox: postbox, network: network, entry: entry)
|
||||
|> then(postbox.transaction { transaction -> Void in
|
||||
transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry)
|
||||
})
|
||||
disposable.set(signal.start())
|
||||
}
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
let disposables = helper.with { helper -> [Disposable] in
|
||||
return helper.reset()
|
||||
}
|
||||
for disposable in disposables {
|
||||
disposable.dispose()
|
||||
}
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func synchronizeConsumeMessageContents(transaction: Transaction, postbox: Postbox, network: Network, stateManager: AccountStateManager, id: MessageId) -> Signal<Void, NoError> {
|
||||
if id.peerId.namespace == Namespaces.Peer.CloudUser || id.peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
return network.request(Api.functions.messages.readMessageContents(id: [id.id]))
|
||||
|
|
@ -219,6 +282,44 @@ private func synchronizeConsumeMessageContents(transaction: Transaction, postbox
|
|||
}
|
||||
}
|
||||
|
||||
private func synchronizeReadMessageReactions(transaction: Transaction, postbox: Postbox, network: Network, stateManager: AccountStateManager, id: MessageId) -> Signal<Void, NoError> {
|
||||
guard let inputPeer = transaction.getPeer(id.peerId).flatMap(apiInputPeer) else {
|
||||
return .complete()
|
||||
}
|
||||
return network.request(Api.functions.messages.readReactions(peer: inputPeer))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.AffectedHistory?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
if let result = result {
|
||||
switch result {
|
||||
case let .affectedHistory(pts, ptsCount, _):
|
||||
stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)])
|
||||
}
|
||||
}
|
||||
return postbox.transaction { transaction -> Void in
|
||||
transaction.setPendingMessageAction(type: .readReaction, id: id, action: nil)
|
||||
transaction.updateMessage(id, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
|
||||
}
|
||||
var attributes = currentMessage.attributes
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ReactionsMessageAttribute, attribute.hasUnseen {
|
||||
attributes[j] = attribute.withAllSeen()
|
||||
break loop
|
||||
}
|
||||
}
|
||||
var updatedTags = currentMessage.tags
|
||||
updatedTags.remove(.unseenReaction)
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func synchronizeUnseenPersonalMentionsTag(postbox: Postbox, network: Network, entry: InvalidatedMessageHistoryTagsSummaryEntry) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let peer = transaction.getPeer(entry.key.peerId), let inputPeer = apiInputPeer(peer) {
|
||||
|
|
@ -235,7 +336,7 @@ private func synchronizeUnseenPersonalMentionsTag(postbox: Postbox, network: Net
|
|||
let apiTopMessage: Int32
|
||||
let apiUnreadMentionsCount: Int32
|
||||
switch dialog {
|
||||
case let .dialog(_, _, topMessage, _, _, _, unreadMentionsCount, _, _, _, _):
|
||||
case let .dialog(_, _, topMessage, _, _, _, unreadMentionsCount, _, _, _, _, _):
|
||||
apiTopMessage = topMessage
|
||||
apiUnreadMentionsCount = unreadMentionsCount
|
||||
|
||||
|
|
@ -260,3 +361,45 @@ private func synchronizeUnseenPersonalMentionsTag(postbox: Postbox, network: Net
|
|||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
private func synchronizeUnseenReactionsTag(postbox: Postbox, network: Network, entry: InvalidatedMessageHistoryTagsSummaryEntry) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let peer = transaction.getPeer(entry.key.peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return network.request(Api.functions.messages.getPeerDialogs(peers: [.inputDialogPeer(peer: inputPeer)]))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.PeerDialogs?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
if let result = result {
|
||||
switch result {
|
||||
case let .peerDialogs(dialogs, _, _, _, _):
|
||||
if let dialog = dialogs.filter({ $0.peerId == entry.key.peerId }).first {
|
||||
let apiTopMessage: Int32
|
||||
let apiUnreadReactionsCount: Int32
|
||||
switch dialog {
|
||||
case let .dialog(_, _, topMessage, _, _, _, _, unreadReactionsCount, _, _, _, _):
|
||||
apiTopMessage = topMessage
|
||||
apiUnreadReactionsCount = unreadReactionsCount
|
||||
|
||||
case .dialogFolder:
|
||||
assertionFailure()
|
||||
return .complete()
|
||||
}
|
||||
|
||||
return postbox.transaction { transaction -> Void in
|
||||
transaction.replaceMessageTagSummary(peerId: entry.key.peerId, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: apiUnreadReactionsCount, maxId: apiTopMessage)
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,3 +232,31 @@ func markUnseenPersonalMessage(transaction: Transaction, id: MessageId, addSynch
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func markUnseenReactionMessage(transaction: Transaction, id: MessageId, addSynchronizeAction: Bool) {
|
||||
if let message = transaction.getMessage(id) {
|
||||
var consume = false
|
||||
inner: for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReactionsMessageAttribute, !attribute.hasUnseen {
|
||||
consume = true
|
||||
break inner
|
||||
}
|
||||
}
|
||||
if consume {
|
||||
transaction.updateMessage(id, update: { currentMessage in
|
||||
var attributes = currentMessage.attributes
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ReactionsMessageAttribute {
|
||||
attributes[j] = attribute.withAllSeen()
|
||||
break loop
|
||||
}
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
|
||||
if addSynchronizeAction {
|
||||
transaction.setPendingMessageAction(type: .readReaction, id: id, action: ReadReactionAction())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox,
|
|||
var apiChannelPts: Int32?
|
||||
let apiNotificationSettings: Api.PeerNotifySettings
|
||||
switch dialog {
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, peerNotificationSettings, pts, _, _):
|
||||
case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, peerNotificationSettings, pts, _, _):
|
||||
apiPeer = peer
|
||||
apiTopMessage = topMessage
|
||||
apiReadInboxMaxId = readInboxMaxId
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import SwiftSignalKit
|
|||
import TelegramApi
|
||||
import MtProtoKit
|
||||
|
||||
public func updateMessageReactionsInteractively(account: Account, messageId: MessageId, reaction: String?) -> Signal<Never, NoError> {
|
||||
public func updateMessageReactionsInteractively(account: Account, messageId: MessageId, reaction: String?, isLarge: Bool) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.setPendingMessageAction(type: .updateReaction, id: messageId, action: UpdateMessageReactionsAction())
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
|
|
@ -19,7 +19,7 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes
|
|||
break loop
|
||||
}
|
||||
}
|
||||
attributes.append(PendingReactionsMessageAttribute(accountPeerId: account.peerId, value: reaction))
|
||||
attributes.append(PendingReactionsMessageAttribute(accountPeerId: account.peerId, value: reaction, isLarge: isLarge))
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ private enum RequestUpdateMessageReactionError {
|
|||
}
|
||||
|
||||
private func requestUpdateMessageReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal<Never, RequestUpdateMessageReactionError> {
|
||||
return postbox.transaction { transaction -> (Peer, String?)? in
|
||||
return postbox.transaction { transaction -> (Peer, String?, Bool)? in
|
||||
guard let peer = transaction.getPeer(messageId.peerId) else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -39,17 +39,19 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st
|
|||
return nil
|
||||
}
|
||||
var value: String?
|
||||
var isLarge: Bool = false
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? PendingReactionsMessageAttribute {
|
||||
value = attribute.value
|
||||
isLarge = attribute.isLarge
|
||||
break
|
||||
}
|
||||
}
|
||||
return (peer, value)
|
||||
return (peer, value, isLarge)
|
||||
}
|
||||
|> castError(RequestUpdateMessageReactionError.self)
|
||||
|> mapToSignal { peerAndValue in
|
||||
guard let (peer, value) = peerAndValue else {
|
||||
guard let (peer, value, isLarge) = peerAndValue else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
guard let inputPeer = apiInputPeer(peer) else {
|
||||
|
|
@ -58,7 +60,16 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st
|
|||
if messageId.namespace != Namespaces.Message.Cloud {
|
||||
return .fail(.generic)
|
||||
}
|
||||
let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendReaction(flags: value == nil ? 0 : 1, peer: inputPeer, msgId: messageId.id, reaction: value))
|
||||
|
||||
var flags: Int32 = 0
|
||||
if value != nil {
|
||||
flags |= 1 << 0
|
||||
if isLarge {
|
||||
flags |= 1 << 1
|
||||
}
|
||||
}
|
||||
|
||||
let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendReaction(flags: flags, peer: inputPeer, msgId: messageId.id, reaction: value))
|
||||
|> mapError { _ -> RequestUpdateMessageReactionError in
|
||||
return .generic
|
||||
}
|
||||
|
|
@ -382,7 +393,7 @@ public final class EngineMessageReactionListContext {
|
|||
|> mapToSignal { result -> Signal<InternalState, NoError> in
|
||||
return account.postbox.transaction { transaction -> InternalState in
|
||||
switch result {
|
||||
case let .messageReactionsList(_, count, reactions, users, nextOffset):
|
||||
case let .messageReactionsList(_, count, reactions, chats, users, nextOffset):
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
|
||||
|
|
@ -393,6 +404,11 @@ public final class EngineMessageReactionListContext {
|
|||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
for chat in chats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
|
|
@ -402,8 +418,8 @@ public final class EngineMessageReactionListContext {
|
|||
var items: [EngineMessageReactionListContext.Item] = []
|
||||
for reaction in reactions {
|
||||
switch reaction {
|
||||
case let .messageUserReaction(userId, reaction):
|
||||
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) {
|
||||
case let .messagePeerReaction(_, peer, reaction):
|
||||
if let peer = transaction.getPeer(peer.peerId) {
|
||||
items.append(EngineMessageReactionListContext.Item(peer: EnginePeer(peer), reaction: reaction))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 137
|
||||
return 138
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ private func dialogReadState(network: Network, postbox: Postbox, peerId: PeerId)
|
|||
let apiMarkedUnread: Bool
|
||||
var apiChannelPts: Int32 = 0
|
||||
switch dialog {
|
||||
case let .dialog(flags, _, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, pts, _, _):
|
||||
case let .dialog(flags, _, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, _, pts, _, _):
|
||||
apiTopMessage = topMessage
|
||||
apiReadInboxMaxId = readInboxMaxId
|
||||
apiReadOutboxMaxId = readOutboxMaxId
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ extension Api.Peer {
|
|||
extension Api.Dialog {
|
||||
var peerId: PeerId? {
|
||||
switch self {
|
||||
case let .dialog(_, peer, _, _, _, _, _, _, _, _, _):
|
||||
case let .dialog(_, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
return peer.peerId
|
||||
case .dialogFolder:
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -19,3 +19,22 @@ public final class ConsumePersonalMessageAction: PendingMessageActionData {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ReadReactionAction: PendingMessageActionData {
|
||||
public init() {
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
}
|
||||
|
||||
public func isEqual(to: PendingMessageActionData) -> Bool {
|
||||
if let _ = to as? ReadReactionAction {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,8 +108,9 @@ public extension MessageTags {
|
|||
static let photo = MessageTags(rawValue: 1 << 8)
|
||||
static let video = MessageTags(rawValue: 1 << 9)
|
||||
static let pinned = MessageTags(rawValue: 1 << 10)
|
||||
static let unseenReaction = MessageTags(rawValue: 1 << 11)
|
||||
|
||||
static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif, .photo, .video, .pinned]
|
||||
static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif, .photo, .video, .pinned, .unseenReaction]
|
||||
}
|
||||
|
||||
public extension GlobalMessageTags {
|
||||
|
|
@ -128,6 +129,7 @@ public extension PendingMessageActionType {
|
|||
static let consumeUnseenPersonalMessage = PendingMessageActionType(rawValue: 0)
|
||||
static let updateReaction = PendingMessageActionType(rawValue: 1)
|
||||
static let sendScheduledMessageImmediately = PendingMessageActionType(rawValue: 2)
|
||||
static let readReaction = PendingMessageActionType(rawValue: 3)
|
||||
}
|
||||
|
||||
public let peerIdNamespacesWithInitialCloudMessageHoles = [Namespaces.Peer.CloudUser, Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel]
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@ public struct MessageReaction: Equatable, PostboxCoding {
|
|||
public var isSelected: Bool
|
||||
|
||||
public init(value: String, count: Int32, isSelected: Bool) {
|
||||
var value = value
|
||||
|
||||
if value == "❤️" {
|
||||
value = "❤"
|
||||
}
|
||||
|
||||
self.value = value
|
||||
self.count = count
|
||||
self.isSelected = isSelected
|
||||
|
|
@ -33,20 +27,28 @@ public struct MessageReaction: Equatable, PostboxCoding {
|
|||
public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
||||
public struct RecentPeer: Equatable, PostboxCoding {
|
||||
public var value: String
|
||||
public var isLarge: Bool
|
||||
public var isUnseen: Bool
|
||||
public var peerId: PeerId
|
||||
|
||||
public init(value: String, peerId: PeerId) {
|
||||
public init(value: String, isLarge: Bool, isUnseen: Bool, peerId: PeerId) {
|
||||
self.value = value
|
||||
self.isLarge = isLarge
|
||||
self.isUnseen = isUnseen
|
||||
self.peerId = peerId
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.value = decoder.decodeStringForKey("v", orElse: "")
|
||||
self.isLarge = decoder.decodeInt32ForKey("l", orElse: 0) != 0
|
||||
self.isUnseen = decoder.decodeInt32ForKey("u", orElse: 0) != 0
|
||||
self.peerId = PeerId(decoder.decodeInt64ForKey("p", orElse: 0))
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeString(self.value, forKey: "v")
|
||||
encoder.encodeInt32(self.isLarge ? 1 : 0, forKey: "l")
|
||||
encoder.encodeInt32(self.isUnseen ? 1 : 0, forKey: "u")
|
||||
encoder.encodeInt64(self.peerId.toInt64(), forKey: "p")
|
||||
}
|
||||
}
|
||||
|
|
@ -89,11 +91,33 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public var hasUnseen: Bool {
|
||||
for recentPeer in self.recentPeers {
|
||||
if recentPeer.isUnseen {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func withAllSeen() -> ReactionsMessageAttribute {
|
||||
return ReactionsMessageAttribute(
|
||||
canViewList: self.canViewList,
|
||||
reactions: self.reactions,
|
||||
recentPeers: self.recentPeers.map { recentPeer in
|
||||
var recentPeer = recentPeer
|
||||
recentPeer.isUnseen = false
|
||||
return recentPeer
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public final class PendingReactionsMessageAttribute: MessageAttribute {
|
||||
public let accountPeerId: PeerId?
|
||||
public let value: String?
|
||||
public let isLarge: Bool
|
||||
|
||||
public var associatedPeerIds: [PeerId] {
|
||||
if let accountPeerId = self.accountPeerId {
|
||||
|
|
@ -103,14 +127,16 @@ public final class PendingReactionsMessageAttribute: MessageAttribute {
|
|||
}
|
||||
}
|
||||
|
||||
public init(accountPeerId: PeerId?, value: String?) {
|
||||
public init(accountPeerId: PeerId?, value: String?, isLarge: Bool) {
|
||||
self.accountPeerId = accountPeerId
|
||||
self.value = value
|
||||
self.isLarge = isLarge
|
||||
}
|
||||
|
||||
required public init(decoder: PostboxDecoder) {
|
||||
self.accountPeerId = decoder.decodeOptionalInt64ForKey("ap").flatMap(PeerId.init)
|
||||
self.value = decoder.decodeOptionalStringForKey("v")
|
||||
self.isLarge = decoder.decodeInt32ForKey("l", orElse: 0) != 0
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
|
|
@ -124,5 +150,6 @@ public final class PendingReactionsMessageAttribute: MessageAttribute {
|
|||
} else {
|
||||
encoder.encodeNil(forKey: "v")
|
||||
}
|
||||
encoder.encodeInt32(self.isLarge ? 1 : 0, forKey: "l")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
|||
upgradedMessageHoles: upgradedMessageHoles,
|
||||
messageThreadHoles: messageThreadHoles,
|
||||
existingMessageTags: MessageTags.all,
|
||||
messageTagsWithSummary: [.unseenPersonalMessage, .pinned, .video, .photo, .gif, .music, .voiceOrInstantVideo, .webPage, .file],
|
||||
messageTagsWithSummary: [.unseenPersonalMessage, .pinned, .video, .photo, .gif, .music, .voiceOrInstantVideo, .webPage, .file, .unseenReaction],
|
||||
existingGlobalMessageTags: GlobalMessageTags.all,
|
||||
peerNamespacesRequiringMessageTextIndex: [Namespaces.Peer.SecretChat],
|
||||
peerSummaryCounterTags: { peer, isContact in
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public final class EngineChatList {
|
|||
public let renderedPeer: EngineRenderedPeer
|
||||
public let presence: EnginePeer.Presence?
|
||||
public let hasUnseenMentions: Bool
|
||||
public let hasUnseenReactions: Bool
|
||||
public let hasFailed: Bool
|
||||
public let isContact: Bool
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ public final class EngineChatList {
|
|||
renderedPeer: EngineRenderedPeer,
|
||||
presence: EnginePeer.Presence?,
|
||||
hasUnseenMentions: Bool,
|
||||
hasUnseenReactions: Bool,
|
||||
hasFailed: Bool,
|
||||
isContact: Bool
|
||||
) {
|
||||
|
|
@ -51,6 +53,7 @@ public final class EngineChatList {
|
|||
self.renderedPeer = renderedPeer
|
||||
self.presence = presence
|
||||
self.hasUnseenMentions = hasUnseenMentions
|
||||
self.hasUnseenReactions = hasUnseenReactions
|
||||
self.hasFailed = hasFailed
|
||||
self.isContact = isContact
|
||||
}
|
||||
|
|
@ -205,7 +208,7 @@ public extension EngineChatList.RelativePosition {
|
|||
extension EngineChatList.Item {
|
||||
convenience init?(_ entry: ChatListEntry) {
|
||||
switch entry {
|
||||
case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedState, renderedPeer, presence, summaryInfo, hasFailed, isContact):
|
||||
case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedState, renderedPeer, presence, tagSummaryInfo, hasFailed, isContact):
|
||||
var draftText: String?
|
||||
if let embeddedState = embeddedState, let _ = embeddedState.overrideChatTimestamp {
|
||||
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
|
||||
|
|
@ -214,6 +217,23 @@ extension EngineChatList.Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hasUnseenMentions = false
|
||||
if let info = tagSummaryInfo[ChatListEntryMessageTagSummaryKey(
|
||||
tag: .unseenPersonalMessage,
|
||||
actionType: PendingMessageActionType.consumeUnseenPersonalMessage
|
||||
)] {
|
||||
hasUnseenMentions = (info.tagSummaryCount ?? 0) > (info.actionsSummaryCount ?? 0)
|
||||
}
|
||||
|
||||
var hasUnseenReactions = false
|
||||
if let info = tagSummaryInfo[ChatListEntryMessageTagSummaryKey(
|
||||
tag: .unseenReaction,
|
||||
actionType: PendingMessageActionType.readReaction
|
||||
)] {
|
||||
hasUnseenReactions = (info.tagSummaryCount ?? 0) > (info.actionsSummaryCount ?? 0)
|
||||
}
|
||||
|
||||
self.init(
|
||||
index: index,
|
||||
messages: messages.map(EngineMessage.init),
|
||||
|
|
@ -222,7 +242,8 @@ extension EngineChatList.Item {
|
|||
draftText: draftText,
|
||||
renderedPeer: EngineRenderedPeer(renderedPeer),
|
||||
presence: presence.flatMap(EnginePeer.Presence.init),
|
||||
hasUnseenMentions: (summaryInfo.tagSummaryCount ?? 0) > (summaryInfo.actionsSummaryCount ?? 0),
|
||||
hasUnseenMentions: hasUnseenMentions,
|
||||
hasUnseenReactions: hasUnseenReactions,
|
||||
hasFailed: hasFailed,
|
||||
isContact: isContact
|
||||
)
|
||||
|
|
|
|||
|
|
@ -72,3 +72,67 @@ func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: Pe
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func _internal_earliestUnseenPersonalReactionMessage(account: Account, peerId: PeerId) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> {
|
||||
return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenReaction, additionalData: [.peerChatState(peerId)])
|
||||
|> mapToSignal { view -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in
|
||||
if view.0.isLoading {
|
||||
return .single(.loading)
|
||||
}
|
||||
if let message = view.0.entries.first?.message {
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
var invalidatedPts: Int32?
|
||||
for data in view.0.additionalData {
|
||||
switch data {
|
||||
case let .peerChatState(_, state):
|
||||
if let state = state as? ChannelState {
|
||||
invalidatedPts = state.invalidatedPts
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
if let invalidatedPts = invalidatedPts {
|
||||
var messagePts: Int32?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ChannelMessageStateVersionAttribute {
|
||||
messagePts = attribute.pts
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let messagePts = messagePts {
|
||||
if messagePts < invalidatedPts {
|
||||
return .single(.loading)
|
||||
}
|
||||
}
|
||||
}
|
||||
return .single(.result(message.id))
|
||||
} else {
|
||||
return .single(.result(message.id))
|
||||
}
|
||||
} else {
|
||||
return account.postbox.transaction { transaction -> EarliestUnseenPersonalMentionMessageResult in
|
||||
if let topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) {
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: topId.id)
|
||||
|
||||
transaction.removeHole(peerId: peerId, namespace: Namespaces.Message.Cloud, space: .tag(.unseenReaction), range: 1 ... (Int32.max - 1))
|
||||
let ids = transaction.getMessageIndicesWithTag(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .unseenReaction).map({ $0.id })
|
||||
for id in ids {
|
||||
markUnseenReactionMessage(transaction: transaction, id: id, addSynchronizeAction: false)
|
||||
}
|
||||
}
|
||||
|
||||
return .result(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> take(until: { value in
|
||||
if case .result = value {
|
||||
return SignalTakeAction(passthrough: true, complete: true)
|
||||
} else {
|
||||
return SignalTakeAction(passthrough: true, complete: false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import SwiftSignalKit
|
|||
func _internal_installInteractiveReadMessagesAction(postbox: Postbox, stateManager: AccountStateManager, peerId: PeerId) -> Disposable {
|
||||
return postbox.installStoreMessageAction(peerId: peerId, { messages, transaction in
|
||||
var consumeMessageIds: [MessageId] = []
|
||||
var readReactionIds: [MessageId] = []
|
||||
|
||||
var readMessageIndexByNamespace: [MessageId.Namespace: MessageIndex] = [:]
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ func _internal_installInteractiveReadMessagesAction(postbox: Postbox, stateManag
|
|||
if case let .Id(id) = message.id {
|
||||
var hasUnconsumedMention = false
|
||||
var hasUnconsumedContent = false
|
||||
var hasUnseenReactions = false
|
||||
|
||||
if message.tags.contains(.unseenPersonalMessage) {
|
||||
inner: for attribute in message.attributes {
|
||||
|
|
@ -21,6 +23,8 @@ func _internal_installInteractiveReadMessagesAction(postbox: Postbox, stateManag
|
|||
hasUnconsumedMention = true
|
||||
} else if let attribute = attribute as? ConsumableContentMessageAttribute, !attribute.consumed {
|
||||
hasUnconsumedContent = true
|
||||
} else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen {
|
||||
hasUnseenReactions = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +32,9 @@ func _internal_installInteractiveReadMessagesAction(postbox: Postbox, stateManag
|
|||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
consumeMessageIds.append(id)
|
||||
}
|
||||
if hasUnseenReactions {
|
||||
readReactionIds.append(id)
|
||||
}
|
||||
|
||||
if !message.flags.intersection(.IsIncomingMask).isEmpty {
|
||||
let index = MessageIndex(id: id, timestamp: message.timestamp)
|
||||
|
|
@ -39,19 +46,34 @@ func _internal_installInteractiveReadMessagesAction(postbox: Postbox, stateManag
|
|||
}
|
||||
}
|
||||
|
||||
for id in consumeMessageIds {
|
||||
for id in Set(consumeMessageIds + readReactionIds) {
|
||||
transaction.updateMessage(id, update: { currentMessage in
|
||||
var attributes = currentMessage.attributes
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute {
|
||||
attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: attribute.consumed, pending: true)
|
||||
break loop
|
||||
if consumeMessageIds.contains(id) {
|
||||
mentionsLoop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute {
|
||||
attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: attribute.consumed, pending: true)
|
||||
break mentionsLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
if readReactionIds.contains(id) {
|
||||
reactionsLoop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ReactionsMessageAttribute {
|
||||
attributes[j] = attribute.withAllSeen()
|
||||
break reactionsLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
|
||||
transaction.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: ConsumePersonalMessageAction())
|
||||
if consumeMessageIds.contains(id) {
|
||||
transaction.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: ConsumePersonalMessageAction())
|
||||
}
|
||||
if readReactionIds.contains(id) {
|
||||
transaction.setPendingMessageAction(type: .readReaction, id: id, action: ReadReactionAction())
|
||||
}
|
||||
}
|
||||
|
||||
for (_, index) in readMessageIndexByNamespace {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import Postbox
|
|||
import TelegramApi
|
||||
import SwiftSignalKit
|
||||
|
||||
|
||||
func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messageId: MessageId) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
if let message = transaction.getMessage(messageId), message.flags.contains(.Incoming) {
|
||||
|
|
@ -128,6 +127,39 @@ func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messa
|
|||
}
|
||||
}
|
||||
|
||||
func _internal_markReactionsAsSeenInteractively(postbox: Postbox, messageId: MessageId) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
if let message = transaction.getMessage(messageId), message.tags.contains(.unseenReaction) {
|
||||
var updateMessage = false
|
||||
var updatedAttributes = message.attributes
|
||||
|
||||
for i in 0 ..< updatedAttributes.count {
|
||||
if let attribute = updatedAttributes[i] as? ReactionsMessageAttribute, attribute.hasUnseen {
|
||||
updatedAttributes[i] = attribute.withAllSeen()
|
||||
updateMessage = true
|
||||
|
||||
if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
} else {
|
||||
transaction.setPendingMessageAction(type: .readReaction, id: messageId, action: ReadReactionAction())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if updateMessage {
|
||||
transaction.updateMessage(message.id, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
|
||||
}
|
||||
var tags = currentMessage.tags
|
||||
tags.remove(.unseenReaction)
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: MessageId) {
|
||||
if let message = transaction.getMessage(messageId) {
|
||||
var updateMessage = false
|
||||
|
|
|
|||
|
|
@ -147,6 +147,10 @@ public extension TelegramEngine {
|
|||
public func earliestUnseenPersonalMentionMessage(peerId: PeerId) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> {
|
||||
return _internal_earliestUnseenPersonalMentionMessage(account: self.account, peerId: peerId)
|
||||
}
|
||||
|
||||
public func earliestUnseenPersonalReactionMessage(peerId: PeerId) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> {
|
||||
return _internal_earliestUnseenPersonalReactionMessage(account: self.account, peerId: peerId)
|
||||
}
|
||||
|
||||
public func exportMessageLink(peerId: PeerId, messageId: MessageId, isThread: Bool = false) -> Signal<String?, NoError> {
|
||||
return _internal_exportMessageLink(account: self.account, peerId: peerId, messageId: messageId, isThread: isThread)
|
||||
|
|
|
|||
|
|
@ -647,7 +647,7 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox,
|
|||
|
||||
for dialog in dialogs {
|
||||
switch dialog {
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, _, folderId):
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, notifySettings, pts, _, folderId):
|
||||
let peerId = peer.peerId
|
||||
|
||||
if topMessage != 0 {
|
||||
|
|
@ -704,6 +704,7 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox,
|
|||
transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: .idBased(maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: false)]])
|
||||
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage)
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, maxId: topMessage)
|
||||
|
||||
if let pts = pts {
|
||||
if transaction.getPeerChatState(peerId) == nil {
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ public enum PresentationResourceKey: Int32 {
|
|||
|
||||
case chatHistoryNavigationButtonImage
|
||||
case chatHistoryMentionsButtonImage
|
||||
case chatHistoryReactionsButtonImage
|
||||
case chatHistoryNavigationButtonBadgeImage
|
||||
|
||||
case chatMessageAttachedContentButtonIncoming
|
||||
|
|
|
|||
|
|
@ -673,6 +673,23 @@ public struct PresentationResourcesChat {
|
|||
})
|
||||
}
|
||||
|
||||
public static func chatHistoryReactionsButtonImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.chatHistoryReactionsButtonImage.rawValue, { theme in
|
||||
return generateImage(CGSize(width: 38.0, height: 38.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(theme.chat.historyNavigation.fillColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.5, y: 0.5), size: CGSize(width: size.width - 1.0, height: size.height - 1.0)))
|
||||
context.setLineWidth(0.5)
|
||||
context.setStrokeColor(theme.chat.historyNavigation.strokeColor.cgColor)
|
||||
context.strokeEllipse(in: CGRect(origin: CGPoint(x: 0.25, y: 0.25), size: CGSize(width: size.width - 0.5, height: size.height - 0.5)))
|
||||
|
||||
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/ScheduleIcon"), color: theme.chat.historyNavigation.foregroundColor), let cgImage = image.cgImage {
|
||||
context.draw(cgImage, in: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public static func chatHistoryNavigationButtonBadgeImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.chatHistoryNavigationButtonBadgeImage.rawValue, { theme in
|
||||
return generateStretchableFilledCircleImage(diameter: 18.0, color: theme.chat.historyNavigation.badgeBackgroundColor, strokeColor: theme.chat.historyNavigation.badgeStrokeColor, strokeWidth: 1.0, backgroundColor: nil)
|
||||
|
|
|
|||
|
|
@ -1044,7 +1044,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: selectAll)), items: .single(actions), recognizer: recognizer, gesture: gesture)
|
||||
strongSelf.currentContextController = controller
|
||||
|
||||
controller.reactionSelected = { [weak controller] value in
|
||||
controller.reactionSelected = { [weak controller] value, isLarge in
|
||||
guard let strongSelf = self, let message = messages.first else {
|
||||
return
|
||||
}
|
||||
|
|
@ -1110,7 +1110,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
}
|
||||
}
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction).start()
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction, isLarge: isLarge).start()
|
||||
}
|
||||
|
||||
strongSelf.forEachController({ controller in
|
||||
|
|
@ -1328,7 +1328,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
}
|
||||
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction).start()
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction, isLarge: false).start()
|
||||
}
|
||||
})
|
||||
}, activateMessagePinch: { [weak self] sourceNode in
|
||||
|
|
@ -5864,6 +5864,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
self.chatDisplayNode.navigateButtons.reactionsPressed = { [weak self] in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, case let .peer(peerId) = strongSelf.chatLocation {
|
||||
let signal = strongSelf.context.engine.messages.earliestUnseenPersonalReactionMessage(peerId: peerId)
|
||||
strongSelf.navigationActionDisposable.set((signal |> deliverOnMainQueue).start(next: { result in
|
||||
if let strongSelf = self {
|
||||
switch result {
|
||||
case let .result(messageId):
|
||||
if let messageId = messageId {
|
||||
strongSelf.navigateToMessage(from: nil, to: .id(messageId, nil))
|
||||
}
|
||||
case .loading:
|
||||
break
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId, completion in
|
||||
guard let strongSelf = self, strongSelf.isNodeLoaded else {
|
||||
return
|
||||
|
|
@ -8003,12 +8021,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
})
|
||||
|
||||
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesCount(peerId: peerId) |> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesAndReactionCount(peerId: peerId) |> deliverOnMainQueue).start(next: { [weak self] mentionCount, reactionCount in
|
||||
if let strongSelf = self {
|
||||
if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0
|
||||
strongSelf.chatDisplayNode.navigateButtons.reactionsCount = 0
|
||||
} else {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = count
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = mentionCount
|
||||
strongSelf.chatDisplayNode.navigateButtons.reactionsCount = reactionCount
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -452,6 +452,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
private var canReadHistoryDisposable: Disposable?
|
||||
|
||||
private var messageIdsScheduledForMarkAsSeen = Set<MessageId>()
|
||||
private var messageIdsWithReactionsScheduledForMarkAsSeen = Set<MessageId>()
|
||||
|
||||
private var chatHistoryLocationValue: ChatHistoryLocationInput? {
|
||||
didSet {
|
||||
|
|
@ -486,6 +487,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
|
||||
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
|
||||
private let messageMentionProcessingManager = ChatMessageThrottledProcessingManager(delay: 0.2)
|
||||
private let unseenReactionsProcessingManager = ChatMessageThrottledProcessingManager(delay: 0.2, submitInterval: 0.0)
|
||||
let prefetchManager: InChatPrefetchManager
|
||||
private var currentEarlierPrefetchMessages: [(Message, Media)] = []
|
||||
private var currentLaterPrefetchMessages: [(Message, Media)] = []
|
||||
|
|
@ -726,6 +728,17 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
}
|
||||
}
|
||||
|
||||
self.unseenReactionsProcessingManager.process = { [weak self] messageIds in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.canReadHistoryValue {
|
||||
strongSelf.context.account.viewTracker.updateMarkReactionsSeenForMessageIds(messageIds: messageIds)
|
||||
} else {
|
||||
strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen.formUnion(messageIds)
|
||||
}
|
||||
}
|
||||
|
||||
self.preloadPages = false
|
||||
switch self.mode {
|
||||
case .bubbles:
|
||||
|
|
@ -1328,6 +1341,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
strongSelf.messageIdsScheduledForMarkAsSeen.removeAll()
|
||||
context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds)
|
||||
}
|
||||
|
||||
if strongSelf.canReadHistoryValue && !strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen.isEmpty {
|
||||
let messageIds = strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen
|
||||
strongSelf.messageIdsWithReactionsScheduledForMarkAsSeen.removeAll()
|
||||
context?.account.viewTracker.updateMarkReactionsSeenForMessageIds(messageIds: messageIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -1803,6 +1822,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
var messageIdsWithUnsupportedMedia: [MessageId] = []
|
||||
var messageIdsWithRefreshMedia: [MessageId] = []
|
||||
var messageIdsWithUnseenPersonalMention: [MessageId] = []
|
||||
var messageIdsWithUnseenReactions: [MessageId] = []
|
||||
|
||||
if indexRange.0 <= indexRange.1 {
|
||||
for i in (indexRange.0 ... indexRange.1) {
|
||||
|
|
@ -1819,6 +1839,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
}
|
||||
var contentRequiredValidation = false
|
||||
var mediaRequiredValidation = false
|
||||
var hasUnseenReactions = false
|
||||
for attribute in message.attributes {
|
||||
if attribute is ViewCountMessageAttribute {
|
||||
if message.id.namespace == Namespaces.Message.Cloud {
|
||||
|
|
@ -1832,6 +1853,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
hasUnconsumedContent = true
|
||||
} else if let _ = attribute as? ContentRequiresValidationMessageAttribute {
|
||||
contentRequiredValidation = true
|
||||
} else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen {
|
||||
hasUnseenReactions = true
|
||||
}
|
||||
}
|
||||
for media in message.media {
|
||||
|
|
@ -1871,6 +1894,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||
}
|
||||
if hasUnseenReactions {
|
||||
messageIdsWithUnseenReactions.append(message.id)
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
|
|
@ -1883,10 +1909,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
for (message, _, _, _, _) in messages {
|
||||
var hasUnconsumedMention = false
|
||||
var hasUnconsumedContent = false
|
||||
var hasUnseenReactions = false
|
||||
if message.tags.contains(.unseenPersonalMessage) {
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ConsumablePersonalMentionMessageAttribute, !attribute.pending {
|
||||
hasUnconsumedMention = true
|
||||
} else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen {
|
||||
hasUnseenReactions = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1906,6 +1935,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||
}
|
||||
if hasUnseenReactions {
|
||||
messageIdsWithUnseenReactions.append(message.id)
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
|
|
@ -2039,6 +2071,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
if !messageIdsWithUnseenPersonalMention.isEmpty {
|
||||
self.messageMentionProcessingManager.add(messageIdsWithUnseenPersonalMention)
|
||||
}
|
||||
if !messageIdsWithUnseenReactions.isEmpty {
|
||||
self.unseenReactionsProcessingManager.add(messageIdsWithUnseenReactions)
|
||||
}
|
||||
if !messageIdsWithPossibleReactions.isEmpty {
|
||||
self.messageWithReactionsProcessingManager.add(messageIdsWithPossibleReactions)
|
||||
}
|
||||
|
|
@ -2347,7 +2382,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
let completion: (Bool, ListViewDisplayedItemRange) -> Void = { [weak self] wasTransformed, visibleRange in
|
||||
if let strongSelf = self {
|
||||
var newIncomingReactions: [MessageId: String] = [:]
|
||||
if case let .peer(peerId) = strongSelf.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser, let previousHistoryView = strongSelf.historyView {
|
||||
if case .peer = strongSelf.chatLocation, let previousHistoryView = strongSelf.historyView {
|
||||
var updatedIncomingReactions: [MessageId: String] = [:]
|
||||
for entry in transition.historyView.filteredEntries {
|
||||
switch entry {
|
||||
|
|
@ -2356,9 +2391,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
continue
|
||||
}
|
||||
if let reactions = message.reactionsAttribute {
|
||||
for reaction in reactions.reactions {
|
||||
if !reaction.isSelected {
|
||||
updatedIncomingReactions[message.id] = reaction.value
|
||||
for recentPeer in reactions.recentPeers {
|
||||
if recentPeer.isUnseen {
|
||||
updatedIncomingReactions[message.id] = recentPeer.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2368,9 +2403,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
continue
|
||||
}
|
||||
if let reactions = message.0.reactionsAttribute {
|
||||
for reaction in reactions.reactions {
|
||||
if !reaction.isSelected {
|
||||
updatedIncomingReactions[message.0.id] = reaction.value
|
||||
for recentPeer in reactions.recentPeers {
|
||||
if recentPeer.isUnseen {
|
||||
updatedIncomingReactions[message.0.id] = recentPeer.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2385,9 +2420,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
if let updatedReaction = updatedIncomingReactions[message.id] {
|
||||
var previousReaction: String?
|
||||
if let reactions = message.reactionsAttribute {
|
||||
for reaction in reactions.reactions {
|
||||
if !reaction.isSelected {
|
||||
previousReaction = reaction.value
|
||||
for recentPeer in reactions.recentPeers {
|
||||
if recentPeer.isUnseen {
|
||||
previousReaction = recentPeer.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2400,9 +2435,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
if let updatedReaction = updatedIncomingReactions[message.0.id] {
|
||||
var previousReaction: String?
|
||||
if let reactions = message.0.reactionsAttribute {
|
||||
for reaction in reactions.reactions {
|
||||
if !reaction.isSelected {
|
||||
previousReaction = reaction.value
|
||||
for recentPeer in reactions.recentPeers {
|
||||
if recentPeer.isUnseen {
|
||||
previousReaction = recentPeer.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2565,8 +2600,14 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
}
|
||||
|
||||
if !newIncomingReactions.isEmpty, let chatDisplayNode = strongSelf.controllerInteraction.chatControllerNode() as? ChatControllerNode {
|
||||
var visibleNewIncomingReactionMessageIds: [MessageId] = []
|
||||
strongSelf.forEachVisibleItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item, let updatedReaction = newIncomingReactions[item.content.firstMessage.id], let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: updatedReaction) {
|
||||
guard let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item, let updatedReaction = newIncomingReactions[item.content.firstMessage.id] else {
|
||||
return
|
||||
}
|
||||
visibleNewIncomingReactionMessageIds.append(item.content.firstMessage.id)
|
||||
|
||||
if let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: updatedReaction) {
|
||||
for reaction in availableReactions.reactions {
|
||||
guard let centerAnimation = reaction.centerAnimation else {
|
||||
continue
|
||||
|
|
@ -2603,6 +2644,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !visibleNewIncomingReactionMessageIds.isEmpty {
|
||||
strongSelf.unseenReactionsProcessingManager.add(visibleNewIncomingReactionMessageIds)
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.hasActiveTransition = false
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ private let badgeFont = Font.regular(13.0)
|
|||
enum ChatHistoryNavigationButtonType {
|
||||
case down
|
||||
case mentions
|
||||
case reactions
|
||||
}
|
||||
|
||||
class ChatHistoryNavigationButtonNode: ASControlNode {
|
||||
|
|
@ -53,6 +54,8 @@ class ChatHistoryNavigationButtonNode: ASControlNode {
|
|||
self.imageNode.image = PresentationResourcesChat.chatHistoryNavigationButtonImage(theme)
|
||||
case .mentions:
|
||||
self.imageNode.image = PresentationResourcesChat.chatHistoryMentionsButtonImage(theme)
|
||||
case .reactions:
|
||||
self.imageNode.image = PresentationResourcesChat.chatHistoryReactionsButtonImage(theme)
|
||||
}
|
||||
self.imageNode.isLayerBacked = true
|
||||
|
||||
|
|
@ -92,6 +95,8 @@ class ChatHistoryNavigationButtonNode: ASControlNode {
|
|||
self.imageNode.image = PresentationResourcesChat.chatHistoryNavigationButtonImage(theme)
|
||||
case .mentions:
|
||||
self.imageNode.image = PresentationResourcesChat.chatHistoryMentionsButtonImage(theme)
|
||||
case .reactions:
|
||||
self.imageNode.image = PresentationResourcesChat.chatHistoryReactionsButtonImage(theme)
|
||||
}
|
||||
self.badgeBackgroundNode.image = PresentationResourcesChat.chatHistoryNavigationButtonBadgeImage(theme)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
private var theme: PresentationTheme
|
||||
private var dateTimeFormat: PresentationDateTimeFormat
|
||||
|
||||
private let reactionsButton: ChatHistoryNavigationButtonNode
|
||||
private let reactionsButtonTapNode: ASDisplayNode
|
||||
private let mentionsButton: ChatHistoryNavigationButtonNode
|
||||
private let mentionsButtonTapNode: ASDisplayNode
|
||||
private let downButton: ChatHistoryNavigationButtonNode
|
||||
|
|
@ -18,6 +20,8 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
var reactionsPressed: (() -> Void)?
|
||||
var reactionsMenu: (() -> Void)?
|
||||
var mentionsPressed: (() -> Void)?
|
||||
var mentionsMenu: (() -> Void)?
|
||||
|
||||
|
|
@ -53,6 +57,20 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
var reactionsCount: Int32 = 0 {
|
||||
didSet {
|
||||
if self.reactionsCount != 0 {
|
||||
self.reactionsButton.badge = compactNumericCountString(Int(self.reactionsCount), decimalSeparator: self.dateTimeFormat.decimalSeparator)
|
||||
} else {
|
||||
self.reactionsButton.badge = ""
|
||||
}
|
||||
|
||||
if (oldValue != 0) != (self.reactionsCount != 0) {
|
||||
let _ = self.updateLayout(transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(theme: PresentationTheme, dateTimeFormat: PresentationDateTimeFormat) {
|
||||
self.theme = theme
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
|
|
@ -62,6 +80,11 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
self.mentionsButton.isHidden = true
|
||||
self.mentionsButtonTapNode = ASDisplayNode()
|
||||
|
||||
self.reactionsButton = ChatHistoryNavigationButtonNode(theme: theme, type: .reactions)
|
||||
self.reactionsButton.alpha = 0.0
|
||||
self.reactionsButton.isHidden = true
|
||||
self.reactionsButtonTapNode = ASDisplayNode()
|
||||
|
||||
self.downButton = ChatHistoryNavigationButtonNode(theme: theme, type: .down)
|
||||
self.downButton.alpha = 0.0
|
||||
self.downButton.isHidden = true
|
||||
|
|
@ -70,6 +93,8 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
|
||||
self.mentionsButton.isUserInteractionEnabled = false
|
||||
|
||||
self.addSubnode(self.reactionsButton)
|
||||
self.addSubnode(self.reactionsButtonTapNode)
|
||||
self.addSubnode(self.mentionsButton)
|
||||
self.addSubnode(self.mentionsButtonTapNode)
|
||||
self.addSubnode(self.downButton)
|
||||
|
|
@ -78,11 +103,17 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let tapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.mentionsTap(_:)))
|
||||
tapRecognizer.tapActionAtPoint = { _ in
|
||||
let reactionsTapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.reactionsTap(_:)))
|
||||
reactionsTapRecognizer.tapActionAtPoint = { _ in
|
||||
return .waitForSingleTap
|
||||
}
|
||||
self.mentionsButtonTapNode.view.addGestureRecognizer(tapRecognizer)
|
||||
self.reactionsButtonTapNode.view.addGestureRecognizer(reactionsTapRecognizer)
|
||||
|
||||
let mentionsTapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.mentionsTap(_:)))
|
||||
mentionsTapRecognizer.tapActionAtPoint = { _ in
|
||||
return .waitForSingleTap
|
||||
}
|
||||
self.mentionsButtonTapNode.view.addGestureRecognizer(mentionsTapRecognizer)
|
||||
}
|
||||
|
||||
func update(theme: PresentationTheme, dateTimeFormat: PresentationDateTimeFormat) {
|
||||
|
|
@ -99,9 +130,10 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
let buttonSize = CGSize(width: 38.0, height: 38.0)
|
||||
let completeSize = CGSize(width: buttonSize.width, height: buttonSize.height * 2.0 + 12.0)
|
||||
var mentionsOffset: CGFloat = 0.0
|
||||
var reactionsOffset: CGFloat = 0.0
|
||||
|
||||
if self.displayDownButton {
|
||||
mentionsOffset = buttonSize.height + 12.0
|
||||
mentionsOffset += buttonSize.height + 12.0
|
||||
|
||||
self.downButton.isHidden = false
|
||||
transition.updateAlpha(node: self.downButton, alpha: 1.0)
|
||||
|
|
@ -117,6 +149,8 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
}
|
||||
|
||||
if self.mentionCount != 0 {
|
||||
reactionsOffset += buttonSize.height + 12.0
|
||||
|
||||
self.mentionsButton.isHidden = false
|
||||
transition.updateAlpha(node: self.mentionsButton, alpha: 1.0)
|
||||
transition.updateTransformScale(node: self.mentionsButton, scale: 1.0)
|
||||
|
|
@ -132,11 +166,30 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
self.mentionsButtonTapNode.isHidden = true
|
||||
}
|
||||
|
||||
if self.reactionsCount != 0 {
|
||||
self.reactionsButton.isHidden = false
|
||||
transition.updateAlpha(node: self.reactionsButton, alpha: 1.0)
|
||||
transition.updateTransformScale(node: self.reactionsButton, scale: 1.0)
|
||||
self.reactionsButtonTapNode.isHidden = false
|
||||
} else {
|
||||
transition.updateAlpha(node: self.reactionsButton, alpha: 0.0, completion: { [weak self] completed in
|
||||
guard let strongSelf = self, completed else {
|
||||
return
|
||||
}
|
||||
strongSelf.reactionsButton.isHidden = true
|
||||
})
|
||||
transition.updateTransformScale(node: self.reactionsButton, scale: 0.2)
|
||||
self.reactionsButtonTapNode.isHidden = true
|
||||
}
|
||||
|
||||
transition.updatePosition(node: self.downButton, position: CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height), size: buttonSize).center)
|
||||
|
||||
transition.updatePosition(node: self.mentionsButton, position: CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset), size: buttonSize).center)
|
||||
self.mentionsButtonTapNode.frame = CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset), size: buttonSize)
|
||||
|
||||
transition.updatePosition(node: self.reactionsButton, position: CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset - reactionsOffset), size: buttonSize).center)
|
||||
self.reactionsButtonTapNode.frame = CGRect(origin: CGPoint(x: 0.0, y: completeSize.height - buttonSize.height - mentionsOffset), size: buttonSize)
|
||||
|
||||
return completeSize
|
||||
}
|
||||
|
||||
|
|
@ -154,6 +207,16 @@ final class ChatHistoryNavigationButtons: ASDisplayNode {
|
|||
return nil
|
||||
}
|
||||
|
||||
@objc private func reactionsTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
if case .ended = recognizer.state, let gesture = recognizer.lastRecognizedGestureAndLocation?.0 {
|
||||
if case .tap = gesture {
|
||||
self.reactionsPressed?()
|
||||
} else if case .longTap = gesture {
|
||||
self.reactionsMenu?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func mentionsTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
if case .ended = recognizer.state, let gesture = recognizer.lastRecognizedGestureAndLocation?.0 {
|
||||
if case .tap = gesture {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ final class ChatMessageThrottledProcessingManager {
|
|||
|
||||
for id in messageIds {
|
||||
if let processedTimestamp = self.processed[id] {
|
||||
if let submitInterval = self.submitInterval, (timestamp - processedTimestamp) >= submitInterval {
|
||||
if let submitInterval = self.submitInterval, (submitInterval.isZero || (timestamp - processedTimestamp) >= submitInterval) {
|
||||
self.processed[id] = timestamp
|
||||
self.processedList.append(id)
|
||||
self.buffer.insert(id)
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ private enum ChatListSearchEntry: Comparable, Identifiable {
|
|||
isRemovedFromTotalUnreadCount: false,
|
||||
presence: nil,
|
||||
hasUnseenMentions: false,
|
||||
hasUnseenReactions: false,
|
||||
draftState: nil,
|
||||
inputActivities: nil,
|
||||
promoInfo: nil,
|
||||
|
|
@ -230,7 +231,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
|||
return
|
||||
}
|
||||
switch item.content {
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if let message = messages.first {
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue