Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2025-08-18 13:48:46 +04:00
commit f828becdb2
71 changed files with 1871 additions and 609 deletions

View file

@ -552,7 +552,42 @@ public final class NavigateToChatControllerParams {
public let customChatNavigationStack: [EnginePeer.Id]?
public let skipAgeVerification: Bool
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: Location, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, botAppStart: ChatControllerInitialBotAppStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: ChatControllerActivateInput? = nil, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, useBackAnimation: Bool = false, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: NavigateToChatControllerParams.ReportReason? = nil, animated: Bool = true, forceAnimatedScroll: Bool = false, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = [], changeColors: Bool = false, setupController: @escaping (ChatController) -> Void = { _ in }, pushController: ((ChatController, Bool, @escaping () -> Void) -> Void)? = nil, completion: @escaping (ChatController) -> Void = { _ in }, chatListCompletion: @escaping (ChatListController) -> Void = { _ in }, forceOpenChat: Bool = false, customChatNavigationStack: [EnginePeer.Id]? = nil, skipAgeVerification: Bool = false) {
public init(
navigationController: NavigationController,
chatController: ChatController? = nil,
context: AccountContext,
chatLocation: Location,
chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil),
subject: ChatControllerSubject? = nil,
botStart: ChatControllerInitialBotStart? = nil,
attachBotStart: ChatControllerInitialAttachBotStart? = nil,
botAppStart: ChatControllerInitialBotAppStart? = nil,
updateTextInputState: ChatTextInputState? = nil,
activateInput: ChatControllerActivateInput? = nil,
keepStack: NavigateToChatKeepStack = .default,
useExisting: Bool = true,
useBackAnimation: Bool = false,
purposefulAction: (() -> Void)? = nil,
scrollToEndIfExists: Bool = false,
activateMessageSearch: (ChatSearchDomain, String)? = nil,
peekData: ChatPeekTimeout? = nil,
peerNearbyData: ChatPeerNearbyData? = nil,
reportReason: NavigateToChatControllerParams.ReportReason? = nil,
animated: Bool = true,
forceAnimatedScroll: Bool = false,
options: NavigationAnimationOptions = [],
parentGroupId: PeerGroupId? = nil,
chatListFilter: Int32? = nil,
chatNavigationStack: [ChatNavigationStackItem] = [],
changeColors: Bool = false,
setupController: @escaping (ChatController) -> Void = { _ in },
pushController: ((ChatController, Bool, @escaping () -> Void) -> Void)? = nil,
completion: @escaping (ChatController) -> Void = { _ in },
chatListCompletion: @escaping (ChatListController) -> Void = { _ in },
forceOpenChat: Bool = false,
customChatNavigationStack: [EnginePeer.Id]? = nil,
skipAgeVerification: Bool = false
) {
self.navigationController = navigationController
self.chatController = chatController
self.chatLocationContextHolder = chatLocationContextHolder

View file

@ -800,6 +800,7 @@ public enum ChatControllerSubject: Equatable {
case pinnedMessages(id: EngineMessage.Id?)
case messageOptions(peerIds: [EnginePeer.Id], ids: [EngineMessage.Id], info: MessageOptionsInfo)
case customChatContents(contents: ChatCustomContentsProtocol)
case botForumThread(forumId: EnginePeer.Id, threadId: Int64)
public static func ==(lhs: ChatControllerSubject, rhs: ChatControllerSubject) -> Bool {
switch lhs {
@ -833,6 +834,12 @@ public enum ChatControllerSubject: Equatable {
} else {
return false
}
case let .botForumThread(forumId, threadId):
if case .botForumThread(forumId, threadId) = rhs {
return true
} else {
return false
}
}
}

View file

@ -587,7 +587,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
}
}
public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: Int64, isPinned: Bool?, isClosed: Bool?, chatListController: ViewController?, joined: Bool, canSelect: Bool, customEdit: ((ContextController) -> Void)? = nil, customPinUnpin: ((ContextController) -> Void)? = nil, reorder: (() -> Void)? = nil) -> Signal<[ContextMenuItem], NoError> {
public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: Int64, isPinned: Bool?, isClosed: Bool?, chatListController: ViewController?, joined: Bool, canSelect: Bool, customEdit: ((ContextController) -> Void)? = nil, customPinUnpin: ((ContextController) -> Void)? = nil, reorder: (() -> Void)? = nil, onDeleted: (() -> Void)? = nil) -> Signal<[ContextMenuItem], NoError> {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
let strings = presentationData.strings
@ -914,32 +914,33 @@ public func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, thr
})))
}
if channel.hasPermission(.deleteAllMessages) {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak chatListController] _, f in
f(.default)
if let chatListController = chatListController as? ChatListControllerImpl {
chatListController.deletePeerThread(peerId: peerId, threadId: threadId)
} else if let chatListController {
let actionSheet = ActionSheetController(presentationData: presentationData)
var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationText, parseMarkdown: true))
items.append(ActionSheetButtonItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationAction, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
let _ = context.engine.peers.removeForumChannelThread(id: peerId, threadId: threadId).startStandalone(completed: {
})
}))
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak chatListController] c, _ in
c?.dismiss(completion: {
if let chatListController = chatListController as? ChatListControllerImpl {
chatListController.deletePeerThread(peerId: peerId, threadId: threadId)
} else if let chatListController {
let actionSheet = ActionSheetController(presentationData: presentationData)
var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationText, parseMarkdown: true))
items.append(ActionSheetButtonItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationAction, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
onDeleted?()
let _ = context.engine.peers.removeForumChannelThread(id: peerId, threadId: threadId).startStandalone(completed: {
})
}))
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
])
chatListController.present(actionSheet, in: .window(.root))
}
chatListController.present(actionSheet, in: .window(.root))
}
})
})))
}
}

View file

@ -1027,144 +1027,174 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
self.chatListDisplayNode.mainContainerNode.peerSelected = { [weak self] peer, threadId, animated, activateInput, promoInfo in
guard let self else {
return
}
var forumSourcePeer: Signal<EnginePeer?, NoError> = .single(nil)
if case let .savedMessagesChats(peerId) = self.location, peerId != self.context.account.peerId {
forumSourcePeer = self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
)
}
let _ = (combineLatest(queue: .mainQueue(),
self.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)])
|> take(1),
forumSourcePeer
)
|> deliverOnMainQueue).start(next: { [weak self] combinedView, forumSourcePeer in
guard let self, let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else {
return
}
guard let navigationController = self.navigationController as? NavigationController else {
Task { @MainActor [weak self] in
guard let self else {
return
}
var peer = peer
var threadId = threadId
if let forumSourcePeer {
threadId = peer.id.toInt64()
peer = forumSourcePeer
var subject: ChatControllerSubject?
if let threadIdValue = threadId, case let .user(user) = peer {
threadId = nil
if case let .known(linkedForumId) = await self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: user.id)
).get(), let linkedForumId {
subject = .botForumThread(forumId: linkedForumId, threadId: threadIdValue)
}
} else if case let .user(user) = peer {
if case let .known(linkedForumId) = await self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: user.id)
).get(), let linkedForumId {
subject = .botForumThread(forumId: linkedForumId, threadId: EngineMessage.newTopicThreadId)
}
}
var scrollToEndIfExists = false
if let layout = self.validLayout, case .regular = layout.metrics.widthClass {
scrollToEndIfExists = true
var forumSourcePeer: Signal<EnginePeer?, NoError> = .single(nil)
if case let .savedMessagesChats(peerId) = self.location, peerId != self.context.account.peerId {
forumSourcePeer = self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
)
}
var openAsInlineForum = true
if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
openAsInlineForum = false
} else if case let .channel(channel) = peer, channel.flags.contains(.displayForumAsTabs) {
openAsInlineForum = false
} else {
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
let _ = (combineLatest(queue: .mainQueue(),
self.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)])
|> take(1),
forumSourcePeer
)
|> deliverOnMainQueue).start(next: { [weak self] combinedView, forumSourcePeer in
guard let self, let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else {
return
}
guard let navigationController = self.navigationController as? NavigationController else {
return
}
var peer = peer
var threadId = threadId
if let forumSourcePeer {
threadId = peer.id.toInt64()
peer = forumSourcePeer
}
var scrollToEndIfExists = false
if let layout = self.validLayout, case .regular = layout.metrics.widthClass {
scrollToEndIfExists = true
}
var openAsInlineForum = true
if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
openAsInlineForum = false
} else if case let .channel(channel) = peer, channel.flags.contains(.displayForumAsTabs) {
openAsInlineForum = false
}
}
if openAsInlineForum, case let .channel(channel) = peer, channel.isForum, threadId == nil {
self.chatListDisplayNode.clearHighlightAnimated(true)
if self.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: channel.id) {
self.setInlineChatList(location: nil)
} else {
self.setInlineChatList(location: .forum(peerId: channel.id))
}
return
}
if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
context: self.context,
chatLocation: .replyThread(ChatReplyThreadMessage(
peerId: peer.id,
threadId: threadId,
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforumPost: channel.isMonoForum,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
unreadCount: 0,
initialFilledHoles: IndexSet(),
initialAnchor: .automatic,
isNotAvailable: false
)),
subject: nil,
keepStack: .always
))
self.chatListDisplayNode.clearHighlightAnimated(true)
} else {
var navigationAnimationOptions: NavigationAnimationOptions = []
var groupId: EngineChatList.Group = .root
if case let .chatList(groupIdValue) = self.location {
groupId = groupIdValue
if case .root = groupIdValue {
navigationAnimationOptions = .removeOnMasterDetails
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
openAsInlineForum = false
}
}
let chatLocation: NavigateToChatControllerParams.Location
chatLocation = .peer(peer)
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: chatLocation, activateInput: (activateInput && !peer.isDeleted) ? .text : nil, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: navigationAnimationOptions, parentGroupId: groupId._asGroup(), chatListFilter: self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id, completion: { [weak self] controller in
guard let self else {
return
if openAsInlineForum, case let .channel(channel) = peer, channel.isForum, threadId == nil {
self.chatListDisplayNode.clearHighlightAnimated(true)
if self.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: channel.id) {
self.setInlineChatList(location: nil)
} else {
self.setInlineChatList(location: .forum(peerId: channel.id))
}
self.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true)
return
}
if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
context: self.context,
chatLocation: .replyThread(ChatReplyThreadMessage(
peerId: peer.id,
threadId: threadId,
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforumPost: channel.isMonoForum,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
unreadCount: 0,
initialFilledHoles: IndexSet(),
initialAnchor: .automatic,
isNotAvailable: false
)),
subject: subject,
keepStack: .always
))
if let promoInfo = promoInfo {
switch promoInfo {
case .proxy:
let _ = (ApplicationSpecificNotice.getProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager)
|> deliverOnMainQueue).startStandalone(next: { [weak self] value in
guard let self else {
return
}
if !value {
controller.displayPromoAnnouncement(text: self.presentationData.strings.DialogList_AdNoticeAlert)
let _ = ApplicationSpecificNotice.setProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager).startStandalone()
}
})
case let .psa(type, _):
let _ = (ApplicationSpecificNotice.getPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id)
|> deliverOnMainQueue).startStandalone(next: { [weak self] value in
guard let self else {
return
}
if !value {
var text = self.presentationData.strings.ChatList_GenericPsaAlert
let key = "ChatList.PsaAlert.\(type)"
if let string = self.presentationData.strings.primaryComponent.dict[key] {
text = string
} else if let string = self.presentationData.strings.secondaryComponent?.dict[key] {
text = string
}
controller.displayPromoAnnouncement(text: text)
let _ = ApplicationSpecificNotice.setPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id).startStandalone()
}
})
self.chatListDisplayNode.clearHighlightAnimated(true)
} else {
var navigationAnimationOptions: NavigationAnimationOptions = []
var groupId: EngineChatList.Group = .root
if case let .chatList(groupIdValue) = self.location {
groupId = groupIdValue
if case .root = groupIdValue {
navigationAnimationOptions = .removeOnMasterDetails
}
}
}))
}
})
let chatLocation: NavigateToChatControllerParams.Location
chatLocation = .peer(peer)
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
context: self.context,
chatLocation: chatLocation,
subject: subject,
activateInput: (activateInput && !peer.isDeleted) ? .text : nil,
scrollToEndIfExists: scrollToEndIfExists,
animated: !scrollToEndIfExists,
options: navigationAnimationOptions,
parentGroupId: groupId._asGroup(),
chatListFilter: self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id, completion: { [weak self] controller in
guard let self else {
return
}
self.chatListDisplayNode.mainContainerNode.currentItemNode.clearHighlightAnimated(true)
if let promoInfo = promoInfo {
switch promoInfo {
case .proxy:
let _ = (ApplicationSpecificNotice.getProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager)
|> deliverOnMainQueue).startStandalone(next: { [weak self] value in
guard let self else {
return
}
if !value {
controller.displayPromoAnnouncement(text: self.presentationData.strings.DialogList_AdNoticeAlert)
let _ = ApplicationSpecificNotice.setProxyAdsAcknowledgment(accountManager: self.context.sharedContext.accountManager).startStandalone()
}
})
case let .psa(type, _):
let _ = (ApplicationSpecificNotice.getPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id)
|> deliverOnMainQueue).startStandalone(next: { [weak self] value in
guard let self else {
return
}
if !value {
var text = self.presentationData.strings.ChatList_GenericPsaAlert
let key = "ChatList.PsaAlert.\(type)"
if let string = self.presentationData.strings.primaryComponent.dict[key] {
text = string
} else if let string = self.presentationData.strings.secondaryComponent?.dict[key] {
text = string
}
controller.displayPromoAnnouncement(text: text)
let _ = ApplicationSpecificNotice.setPsaAcknowledgment(accountManager: self.context.sharedContext.accountManager, peerId: peer.id).startStandalone()
}
})
}
}
}))
}
})
}
}
self.chatListDisplayNode.mainContainerNode.groupSelected = { [weak self] groupId in

View file

@ -580,6 +580,9 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
if threadId == nil, self.interaction.searchTextHighightState != nil, case let .channel(channel) = peerData.peer.peer, channel.isForumOrMonoForum {
threadId = message.threadId
}
if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), let forumTopicData = peerData.forumTopicData {
threadId = forumTopicData.id
}
self.interaction.messageSelected(peer, threadId, message, peerData.promoInfo)
} else if let peer = peerData.peer.peer {
self.interaction.peerSelected(peer, nil, nil, peerData.promoInfo, false)
@ -2509,6 +2512,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
forumThread = (threadInfo.id, threadInfo.info.title, threadInfo.info.icon, threadInfo.info.iconColor, nil, false)
}
}
if let forumTopicData, forumThread == nil, case let .user(user) = itemPeer.chatMainPeer, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum) {
forumThread = (forumTopicData.id, forumTopicData.title, forumTopicData.iconFileId, forumTopicData.iconColor, forumTopicData.threadPeer, forumTopicData.isUnread)
}
let messageText: String
if let currentChatListText = currentChatListText, currentChatListText.0 == text {
@ -3421,7 +3427,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
} else if forumThread != nil || !topForumTopicItems.isEmpty {
if let forumThread = forumThread {
isFirstForumThreadSelectable = forumThread.isUnread
if case let .peer(peer) = item.content, case .user = peer.peer.chatMainPeer {
isFirstForumThreadSelectable = false
} else {
isFirstForumThreadSelectable = forumThread.isUnread
}
forumThreads.append((id: forumThread.id, threadPeer: forumThread.threadPeer, title: NSAttributedString(string: forumThread.title, font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: forumThread.iconId, iconColor: forumThread.iconColor))
}
for topicItem in topForumTopicItems {
@ -3475,7 +3486,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
textMaxWidth -= 18.0
}
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: (authorAttributedString == nil && itemTags.isEmpty) ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: textMaxWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: (authorAttributedString == nil && itemTags.isEmpty && forumThread == nil) ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: textMaxWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
let maxTitleLines: Int
switch item.index {

View file

@ -20,7 +20,7 @@ final class MutableCachedPeerDataView: MutablePostboxView {
}
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
if let cachedPeerData = transaction.currentUpdatedCachedPeerData[self.peerId] {
if let cachedPeerData = transaction.currentUpdatedCachedPeerData[self.peerId]?.updated {
self.cachedPeerData = cachedPeerData
if self.trackAssociatedMessages {
self.associatedMessages.removeAll()

View file

@ -373,7 +373,7 @@ final class ChatListTable: Table {
return result
}
func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: Set<PeerId>, updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) {
func replay(postbox: PostboxImpl, historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: Set<PeerId>, updatedPeerCachedData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)], updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) {
var changedPeerIds = Set<PeerId>()
for peerId in historyOperationsByPeerId.keys {
changedPeerIds.insert(peerId)
@ -384,6 +384,23 @@ final class ChatListTable: Table {
for peerId in updatedChatListInclusions.keys {
changedPeerIds.insert(peerId)
}
for (peerId, update) in updatedPeerCachedData {
if let previous = update.previous {
if self.seedConfiguration.decodeAssociatedChatListPeerId(previous) != self.seedConfiguration.decodeAssociatedChatListPeerId(update.updated) {
changedPeerIds.insert(peerId)
}
} else {
changedPeerIds.insert(peerId)
}
}
var additionalChangedPeerIds = Set<PeerId>()
for peerId in changedPeerIds {
//TODO:release move this to seed configuration
if let peer = postbox.peerTable.get(peerId), peer.id.namespace._internalGetInt32Value() == 2, let associatedPeerId = peer.associatedPeerId, associatedPeerId.namespace._internalGetInt32Value() == 0 {
additionalChangedPeerIds.insert(associatedPeerId)
}
}
changedPeerIds.formUnion(additionalChangedPeerIds)
self.ensureInitialized(groupId: .root)
@ -396,8 +413,8 @@ final class ChatListTable: Table {
let topMessage = messageHistoryTable.topIndex(peerId: peerId)
let embeddedChatStateOverrideTimestamp = peerChatInterfaceStateTable.get(peerId)?.overrideChatTimestamp
let rawTopMessageIndex: MessageIndex?
let topMessageIndex: MessageIndex?
var rawTopMessageIndex: MessageIndex?
var topMessageIndex: MessageIndex?
if let topMessage = topMessage {
var updatedTimestamp = topMessage.timestamp
rawTopMessageIndex = MessageIndex(id: topMessage.id, timestamp: topMessage.timestamp)
@ -413,6 +430,18 @@ final class ChatListTable: Table {
rawTopMessageIndex = nil
}
if let cachedData = postbox.cachedPeerDataTable.get(peerId), let associatedChatListPeerId = self.seedConfiguration.decodeAssociatedChatListPeerId(cachedData) {
if let associatedTopMessage = messageHistoryTable.topIndex(peerId: associatedChatListPeerId) {
if let currentTopMessageIndex = topMessageIndex {
if currentTopMessageIndex.timestamp < associatedTopMessage.timestamp {
topMessageIndex = MessageIndex(id: currentTopMessageIndex.id, timestamp: associatedTopMessage.timestamp)
}
} else {
topMessageIndex = MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 1), timestamp: associatedTopMessage.timestamp)
}
}
}
var updatedIndex = self.indexTable.setTopMessageIndex(peerId: peerId, index: topMessageIndex)
if let updatedInclusion = updatedChatListInclusions[peerId] {
updatedIndex = self.indexTable.setInclusion(peerId: peerId, inclusion: updatedInclusion)

View file

@ -1040,19 +1040,71 @@ public final class ChatListView {
self.groupId = mutableView.groupId
var entries: [ChatListEntry] = []
//TODO:release
var linkedEntries: [PeerId: [MutableChatListEntry.MessageEntryData]] = [:]
if "".isEmpty {
for entry in mutableView.sampledState.entries {
guard case let .MessageEntry(entryData) = entry else {
continue
}
if let peer = entryData.renderedPeer.peer, peer.id.namespace._internalGetInt32Value() == 2, let associatedPeerId = peer.associatedPeerId, associatedPeerId.namespace._internalGetInt32Value() == 0 {
if linkedEntries[associatedPeerId] == nil {
linkedEntries[associatedPeerId] = []
}
linkedEntries[associatedPeerId]?.append(entryData)
}
}
}
for entry in mutableView.sampledState.entries {
switch entry {
case let .MessageEntry(entryData):
if let peer = entryData.renderedPeer.peer, peer.id.namespace._internalGetInt32Value() == 2, let associatedPeerId = peer.associatedPeerId, associatedPeerId.namespace._internalGetInt32Value() == 0 {
continue
}
//TODO:release
var index = entryData.index
var messages = entryData.messages
var readState = entryData.readState
var forumTopicData = entryData.displayAsRegularChat ? nil : entryData.forumTopicData
if let linkedEntries = linkedEntries[entryData.index.messageIndex.id.peerId] {
for entry in linkedEntries {
if entry.index.messageIndex.timestamp >= index.messageIndex.timestamp {
index = ChatListIndex(pinningIndex: index.pinningIndex, messageIndex: MessageIndex(id: index.messageIndex.id, timestamp: entry.index.messageIndex.timestamp))
messages = entry.messages
forumTopicData = entry.forumTopicData
}
if let entryReadState = entry.readState {
if var readStateValue = readState {
var states = readStateValue.state.states
for (namespace, state) in entryReadState.state.states {
if let index = states.firstIndex(where: { $0.0 == namespace }) {
states[index] = (namespace, states[index].1.withAddedCount(state.count))
} else {
states.append((namespace, state))
}
}
readStateValue.state = .init(states: states)
readState = readStateValue
} else {
readState = entryReadState
}
}
}
}
entries.append(.MessageEntry(ChatListEntry.MessageEntryData(
index: entryData.index,
messages: entryData.messages,
readState: entryData.readState,
index: index,
messages: messages,
readState: readState,
isRemovedFromTotalUnreadCount: entryData.isRemovedFromTotalUnreadCount,
embeddedInterfaceState: entryData.embeddedInterfaceState,
renderedPeer: entryData.renderedPeer,
presence: entryData.presence,
summaryInfo: entryData.tagSummaryInfo,
forumTopicData: entryData.displayAsRegularChat ? nil : entryData.forumTopicData,
forumTopicData: forumTopicData,
topForumTopics: entryData.displayAsRegularChat ? [] : entryData.topForumTopics,
hasFailed: entryData.hasFailedMessages,
isContact: entryData.isContact,

View file

@ -651,6 +651,8 @@ public struct MessageGroupInfo: Equatable {
}
public final class Message {
public static let newTopicThreadId: Int64 = Int64(Int32.max - 1)
public final class AssociatedThreadInfo: Equatable {
public let title: String
public let icon: Int64?
@ -905,6 +907,7 @@ public struct MessageThreadKey: Hashable {
public final class StoreMessage {
public let id: StoreMessageId
public let customStableId: UInt32?
public let timestamp: Int32
public let globallyUniqueId: Int64?
public let groupingKey: Int64?
@ -919,8 +922,9 @@ public final class StoreMessage {
public let attributes: [MessageAttribute]
public let media: [Media]
public init(id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
public init(id: MessageId, customStableId: UInt32?, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
self.id = .Id(id)
self.customStableId = customStableId
self.globallyUniqueId = globallyUniqueId
self.groupingKey = groupingKey
self.threadId = threadId
@ -936,8 +940,9 @@ public final class StoreMessage {
self.media = media
}
public init(peerId: PeerId, namespace: MessageId.Namespace, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
public init(peerId: PeerId, namespace: MessageId.Namespace, customStableId: UInt32?, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
self.id = .Partial(peerId, namespace)
self.customStableId = customStableId
self.timestamp = timestamp
self.globallyUniqueId = globallyUniqueId
self.groupingKey = groupingKey
@ -953,8 +958,9 @@ public final class StoreMessage {
self.media = media
}
public init(id: StoreMessageId, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
public init(id: StoreMessageId, customStableId: UInt32?, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
self.id = id
self.customStableId = customStableId
self.timestamp = timestamp
self.globallyUniqueId = globallyUniqueId
self.groupingKey = groupingKey
@ -978,33 +984,38 @@ public final class StoreMessage {
}
}
public func withUpdatedCustomStableId(_ customStableId: UInt32?) -> StoreMessage {
return StoreMessage(id: self.id, customStableId: customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: attributes, media: self.media)
}
public func withUpdatedFlags(_ flags: StoreMessageFlags) -> StoreMessage {
if flags == self.flags {
return self
} else {
return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: attributes, media: self.media)
return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: attributes, media: self.media)
}
}
public func withUpdatedMedia(_ media: [Media]) -> StoreMessage {
return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: self.attributes, media: media)
return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: self.attributes, media: media)
}
public func withUpdatedAttributes(_ attributes: [MessageAttribute]) -> StoreMessage {
return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: attributes, media: self.media)
return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: attributes, media: self.media)
}
public func withUpdatedLocalTags(_ localTags: LocalMessageTags) -> StoreMessage {
if localTags == self.localTags {
return self
} else {
return StoreMessage(id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: self.attributes, media: self.media)
return StoreMessage(id: self.id, customStableId: self.customStableId, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: localTags, forwardInfo: self.forwardInfo, authorId: self.authorId, text: self.text, attributes: self.attributes, media: self.media)
}
}
}
final class InternalStoreMessage {
let id: MessageId
let customStableId: UInt32?
let timestamp: Int32
let globallyUniqueId: Int64?
let groupingKey: Int64?
@ -1024,8 +1035,9 @@ final class InternalStoreMessage {
return MessageIndex(id: self.id, timestamp: self.timestamp)
}
init(id: MessageId, timestamp: Int32, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, customTags: [MemoryBuffer], forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
init(id: MessageId, customStableId: UInt32?, timestamp: Int32, globallyUniqueId: Int64?, groupingKey: Int64?, threadId: Int64?, flags: StoreMessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, customTags: [MemoryBuffer], forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) {
self.id = id
self.customStableId = customStableId
self.timestamp = timestamp
self.globallyUniqueId = globallyUniqueId
self.groupingKey = groupingKey

View file

@ -75,7 +75,7 @@ final class MessageCustomTagHoleIndexTable: Table {
if !self.metadataTable.isPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag) {
self.metadataTable.setPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag)
if let namespaces = self.seedConfiguration.messageThreadHoles[peerId.namespace] {
if let namespaces = self.seedConfiguration.messageThreadHoles(peerId.namespace, threadId) {
for namespace in namespaces {
var operations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:]
self.addInternal(peerId: peerId, threadId: threadId, tag: tag, tagValue: tagValue, namespace: namespace, range: 1 ... (Int32.max - 1), operations: &operations)

View file

@ -79,7 +79,7 @@ final class MessageCustomTagWithTagHoleIndexTable: Table {
if !self.metadataTable.isPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag, regularTag: regularTag) {
self.metadataTable.setPeerCustomTagInitialized(peerId: peerId, threadId: threadId, tag: tag, regularTag: regularTag)
if let namespaces = self.seedConfiguration.messageThreadHoles[peerId.namespace] {
if let namespaces = self.seedConfiguration.messageThreadHoles(peerId.namespace, threadId) {
for namespace in namespaces {
var operations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:]
self.addInternal(peerId: peerId, threadId: threadId, tag: tag, tagValue: tagValue, regularTag: regularTag, namespace: namespace, range: 1 ... (Int32.max - 1), operations: &operations)

View file

@ -431,10 +431,10 @@ final class MessageHistoryTable: Table {
for message in messages {
switch message.id {
case let .Id(id):
internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: self.seedConfiguration.customTagsFromAttributes(message.attributes), forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media))
internalStoreMessages.append(InternalStoreMessage(id: id, customStableId: message.customStableId, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: self.seedConfiguration.customTagsFromAttributes(message.attributes), forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media))
case let .Partial(peerId, namespace):
let id = self.historyMetadataTable.getNextMessageIdAndIncrement(peerId, namespace: namespace)
internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: self.seedConfiguration.customTagsFromAttributes(message.attributes), forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media))
internalStoreMessages.append(InternalStoreMessage(id: id, customStableId: message.customStableId, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, customTags: self.seedConfiguration.customTagsFromAttributes(message.attributes), forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media))
}
}
return internalStoreMessages
@ -1084,7 +1084,7 @@ final class MessageHistoryTable: Table {
var type: Int8 = 0
sharedBuffer.write(&type, offset: 0, length: 1)
var stableId: UInt32 = self.historyMetadataTable.getNextStableMessageIndexId()
var stableId: UInt32 = message.customStableId ?? self.historyMetadataTable.getNextStableMessageIndexId()
sharedBuffer.write(&stableId, offset: 0, length: 4)
var stableVersion: UInt32 = 0
@ -2115,7 +2115,7 @@ final class MessageHistoryTable: Table {
let updatedIndex = MessageIndex(id: index.id, timestamp: timestamp)
let _ = self.justUpdate(index, message: InternalStoreMessage(id: previousMessage.id, timestamp: timestamp, globallyUniqueId: previousMessage.globallyUniqueId, groupingKey: previousMessage.groupingKey, threadId: previousMessage.threadId, flags: StoreMessageFlags(previousMessage.flags), tags: previousMessage.tags, globalTags: previousMessage.globalTags, localTags: previousMessage.localTags, customTags: previousMessage.customTags, forwardInfo: storeForwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributes: parsedAttributes, media: parsedMedia), keepLocalTags: false, sharedKey: self.key(updatedIndex), sharedBuffer: WriteBuffer(), sharedEncoder: PostboxEncoder(), unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, updatedMedia: &updatedMedia)
let _ = self.justUpdate(index, message: InternalStoreMessage(id: previousMessage.id, customStableId: nil, timestamp: timestamp, globallyUniqueId: previousMessage.globallyUniqueId, groupingKey: previousMessage.groupingKey, threadId: previousMessage.threadId, flags: StoreMessageFlags(previousMessage.flags), tags: previousMessage.tags, globalTags: previousMessage.globalTags, localTags: previousMessage.localTags, customTags: previousMessage.customTags, forwardInfo: storeForwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributes: parsedAttributes, media: parsedMedia), keepLocalTags: false, sharedKey: self.key(updatedIndex), sharedBuffer: WriteBuffer(), sharedEncoder: PostboxEncoder(), unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, updatedMedia: &updatedMedia)
return (previousMessage.tags, previousMessage.globalTags)
} else {
return nil

View file

@ -113,7 +113,7 @@ final class MessageHistoryThreadHoleIndexTable: Table {
postboxLog("MessageHistoryThreadHoleIndexTable: Initializing \(peerId) \(threadId)")
self.metadataTable.setIsThreadHoleIndexInitialized(peerId: peerId, threadId: threadId)
if let messageNamespaces = self.seedConfiguration.messageThreadHoles[peerId.namespace] {
if let messageNamespaces = self.seedConfiguration.messageThreadHoles(peerId.namespace, threadId) {
for namespace in messageNamespaces {
var operations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:]
self.add(peerId: peerId, threadId: threadId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1), operations: &operations)

View file

@ -332,6 +332,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
private var userId: Int64?
fileprivate var peerStoryStats: [PeerId: PeerStoryStats] = [:]
fileprivate var typingDraft: Message?
init(
postbox: PostboxImpl,
@ -410,6 +411,8 @@ final class MutableMessageHistoryView: MutablePostboxView {
self.render(postbox: postbox)
let _ = self.updateStoryStats(postbox: postbox)
self.reloadTypingDraft(postbox: postbox)
let _ = self.updateThreadInfos(postbox: postbox, updatedIds: nil)
}
@ -436,6 +439,8 @@ final class MutableMessageHistoryView: MutablePostboxView {
self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles)
let _ = self.updateStoryStats(postbox: postbox)
self.reloadTypingDraft(postbox: postbox)
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
@ -447,14 +452,14 @@ final class MutableMessageHistoryView: MutablePostboxView {
switch self.peerIds {
case let .single(peerId, threadId):
if threadId == nil {
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] {
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId]?.updated {
if updatedData.associatedHistoryMessageId != nil {
self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId)
}
}
}
case let .associated(peerId, associatedId):
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] {
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId]?.updated {
if updatedData.associatedHistoryMessageId != associatedId {
self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId)
}
@ -839,7 +844,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
switch additionalDatas[i] {
case let .cachedPeerData(peerId, currentData):
currentCachedPeerData = currentData
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] {
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId]?.updated {
if currentData?.messageIds != updatedData.messageIds {
updatedCachedPeerDataMessages = true
}
@ -1039,9 +1044,79 @@ final class MutableMessageHistoryView: MutablePostboxView {
}
}
switch self.peerIds {
case let .single(peerId, threadId):
let location = PeerAndThreadId(peerId: peerId, threadId: threadId)
if let typingDraftUpdate = transaction.updatedTypingDrafts[location] {
if let typingDraft = typingDraftUpdate.value {
self.typingDraft = self.renderTypingDraft(postbox: postbox, typingDraft: typingDraft)
} else {
self.typingDraft = nil
}
hasChanges = true
}
case .external:
break
case .associated:
break
}
return hasChanges
}
private func reloadTypingDraft(postbox: PostboxImpl) {
guard case let .single(peerId, threadId) = self.peerIds else {
self.typingDraft = nil
return
}
guard let typingDraft = postbox.currentTypingDrafts[PeerAndThreadId(peerId: peerId, threadId: threadId)] else {
self.typingDraft = nil
return
}
self.typingDraft = self.renderTypingDraft(postbox: postbox, typingDraft: typingDraft)
}
private func renderTypingDraft(postbox: PostboxImpl, typingDraft: PostboxImpl.TypingDraft) -> Message? {
guard case let .single(peerId, threadId) = self.peerIds else {
return nil
}
guard let peer = postbox.peerTable.get(peerId), let author = postbox.peerTable.get(typingDraft.authorId) else {
return nil
}
var peers = SimpleDictionary<PeerId, Peer>()
peers[peer.id] = peer
peers[author.id] = author
return Message(
stableId: typingDraft.stableId,
stableVersion: typingDraft.stableVersion,
id: MessageId(
peerId: peerId,
namespace: 1,
id: Int32.max - 50000),
globallyUniqueId: nil,
groupingKey: nil,
groupInfo: nil,
threadId: threadId,
timestamp: typingDraft.timestamp,
flags: [.Incoming],
tags: [],
globalTags: [],
localTags: [],
customTags: [],
forwardInfo: nil,
author: author,
text: typingDraft.text,
attributes: typingDraft.attributes,
media: [],
peers: peers,
associatedMessages: SimpleDictionary(),
associatedMessageIds: [],
associatedMedia: [:],
associatedThreadInfo: nil,
associatedStories: [:]
)
}
private func updateThreadInfos(postbox: PostboxImpl, updatedIds: Set<MessageHistoryThreadsTable.ItemId>?) -> Bool {
if self.mapReadStatesFromThreads, case let .single(peerId, peerThreadId) = self.peerIds, peerThreadId == nil {
switch self.sampledState {
@ -1446,6 +1521,16 @@ public final class MessageHistoryView: PostboxView {
}
}
if !self.holeLater, let typingDraft = mutableView.typingDraft {
entries.append(MessageHistoryEntry(
message: typingDraft,
isRead: false,
location: nil,
monthLocation: nil,
attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)
))
}
self.entries = entries
self.peerStoryStats = mutableView.peerStoryStats
}

View file

@ -149,7 +149,7 @@ final class MutablePeerView: MutablePostboxView {
var peersUpdated = false
var updateMessages = false
if let cachedData = updatedCachedPeerData[self.contactPeerId], self.cachedData == nil || !self.cachedData!.isEqual(to: cachedData) {
if let cachedData = updatedCachedPeerData[self.contactPeerId]?.updated, self.cachedData == nil || !self.cachedData!.isEqual(to: cachedData) {
if self.cachedData?.messageIds != cachedData.messageIds {
updateMessages = true
}
@ -286,7 +286,7 @@ final class MutablePeerView: MutablePostboxView {
}
if let associatedPeerId = peer.associatedPeerId {
if let value = updatedCachedPeerData[associatedPeerId] {
if let value = updatedCachedPeerData[associatedPeerId]?.updated {
if let current = self.associatedCachedData[associatedPeerId] {
if !current.isEqual(to: value) {
self.associatedCachedData[associatedPeerId] = value

View file

@ -1393,6 +1393,27 @@ public final class Transaction {
assert(!self.disposed)
self.postbox!.reindexSavedMessagesCustomTagsWithTagsIfNeeded(peerId: peerId, threadId: threadId, tag: tag)
}
public func getCurrentTypingDraft(location: PeerAndThreadId) -> (id: Int64, stableId: UInt32, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])? {
assert(!self.disposed)
if let value = self.postbox!.currentTypingDrafts[location] {
return (
value.id,
value.stableId,
value.authorId,
value.timestamp,
value.text,
value.attributes
)
} else {
return nil
}
}
public func combineTypingDrafts(locations: Set<PeerAndThreadId>, update: (PeerAndThreadId, (id: Int64, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) -> (id: Int64, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) {
assert(!self.disposed)
self.postbox!.combineTypingDrafts(locations: locations, update: update)
}
}
public enum PostboxResult {
@ -1594,7 +1615,7 @@ final class PostboxImpl {
private var currentUpdatedPeers: [PeerId: Peer] = [:]
private var currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)] = [:]
private var currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp] = [:]
private var currentUpdatedCachedPeerData: [PeerId: CachedPeerData] = [:]
private var currentUpdatedCachedPeerData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)] = [:]
private var currentUpdatedPeerPresences: [PeerId: PeerPresence] = [:]
private var currentUpdatedPeerChatListEmbeddedStates = Set<PeerId>()
private var currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] = [:]
@ -1635,6 +1656,71 @@ final class PostboxImpl {
private var currentStoryTopItemEvents: [StoryTopItemsTable.Event] = []
private var currentStoryEvents: [StoryTable.Event] = []
struct TypingDraft: Equatable {
var id: Int64
var stableId: UInt32
var stableVersion: UInt32
var authorId: PeerId
var timestamp: Int32
var text: String
var attributes: [MessageAttribute]
var addedAtTimestamp: Double
init(id: Int64, stableId: UInt32, stableVersion: UInt32, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute], addedAtTimestamp: Double) {
self.id = id
self.stableId = stableId
self.stableVersion = stableVersion
self.authorId = authorId
self.timestamp = timestamp
self.text = text
self.attributes = attributes
self.addedAtTimestamp = addedAtTimestamp
}
static func ==(lhs: TypingDraft, rhs: TypingDraft) -> Bool {
if lhs.id != rhs.id {
return false
}
if lhs.stableId != rhs.stableId {
return false
}
if lhs.stableVersion != rhs.stableVersion {
return false
}
if lhs.authorId != rhs.authorId {
return false
}
if lhs.timestamp != rhs.timestamp {
return false
}
if lhs.text != rhs.text {
return false
}
if lhs.attributes.count != rhs.attributes.count {
return false
}
for i in 0 ..< lhs.attributes.count {
let lhsEncoder = PostboxEncoder()
lhs.attributes[i].encode(lhsEncoder)
let rhsEncoder = PostboxEncoder()
rhs.attributes[i].encode(rhsEncoder)
if lhsEncoder.makeData() != rhsEncoder.makeData() {
return false
}
}
return true
}
}
struct TypingDraftUpdate {
var value: TypingDraft?
}
fileprivate(set) var currentTypingDrafts: [PeerAndThreadId: TypingDraft] = [:]
private var currentUpdatedTypingDrafts: [PeerAndThreadId: TypingDraftUpdate] = [:]
private var nextTypingDraftExpirationTimestamp: Double?
private var nextTypingDraftExpirationTimer: SwiftSignalKit.Timer?
var hiddenChatIds: Set<PeerId> {
if self.currentHiddenChatIds.isEmpty {
return Set()
@ -2008,7 +2094,7 @@ final class PostboxImpl {
if let forwardInfo = message.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)
}
return .update(StoreMessage(id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: storeForwardInfo, authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media))
return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: storeForwardInfo, authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media))
} else {
return .skip
}
@ -2019,6 +2105,7 @@ final class PostboxImpl {
}
deinit {
self.nextTypingDraftExpirationTimer?.invalidate()
if let tempDir = self.tempDir {
TempBox.shared.dispose(tempDir)
}
@ -2401,6 +2488,89 @@ final class PostboxImpl {
}
}
fileprivate func combineTypingDrafts(locations: Set<PeerAndThreadId>, update: (PeerAndThreadId, (id: Int64, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) -> (id: Int64, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?) {
for location in locations {
var updated: (id: Int64, authorId: PeerId, timestamp: Int32, text: String, attributes: [MessageAttribute])?
let current = self.currentTypingDrafts[location]
if let current {
updated = update(location, (current.id, current.authorId, current.timestamp, current.text, current.attributes))
} else {
updated = update(location, nil)
}
if let updated {
let stableId: UInt32
let stableVersion: UInt32
if let current, current.id == updated.id {
stableId = current.stableId
stableVersion = 100000 + current.stableVersion + 1
} else {
stableId = self.messageHistoryMetadataTable.getNextStableMessageIndexId()
stableVersion = 100000
}
let mappedDraft = TypingDraft(id: updated.id, stableId: stableId, stableVersion: stableVersion, authorId: updated.authorId, timestamp: updated.timestamp, text: updated.text, attributes: updated.attributes, addedAtTimestamp: CFAbsoluteTimeGetCurrent())
if self.currentTypingDrafts[location] != mappedDraft {
self.currentTypingDrafts[location] = mappedDraft
self.currentUpdatedTypingDrafts[location] = TypingDraftUpdate(value: mappedDraft)
}
} else if self.currentTypingDrafts[location] != nil {
self.currentTypingDrafts.removeValue(forKey: location)
self.currentUpdatedTypingDrafts[location] = TypingDraftUpdate(value: nil)
}
}
}
private func restartTypingDraftExpirationTimerIfNeeded() {
let expirationTimeout: Double = 10.0
var nextTypingDraftExpirationTimestamp: Double?
for (_, draft) in self.currentTypingDrafts {
if let nextTypingDraftExpirationTimestampValue = nextTypingDraftExpirationTimestamp {
nextTypingDraftExpirationTimestamp = min(draft.addedAtTimestamp + expirationTimeout, nextTypingDraftExpirationTimestampValue)
} else {
nextTypingDraftExpirationTimestamp = draft.addedAtTimestamp + expirationTimeout
}
}
if let nextTypingDraftExpirationTimestamp {
if self.nextTypingDraftExpirationTimer == nil || nextTypingDraftExpirationTimestamp != self.nextTypingDraftExpirationTimestamp {
let timeout = nextTypingDraftExpirationTimestamp - CFAbsoluteTimeGetCurrent()
self.nextTypingDraftExpirationTimer?.invalidate()
self.nextTypingDraftExpirationTimer = SwiftSignalKit.Timer(timeout: max(0.0, timeout - 0.1), repeat: false, completion: { [weak self] in
guard let self else {
return
}
let _ = self.transaction { _ in
self.processTypingDraftExpirations(expirationTimeout: expirationTimeout)
}.startStandalone()
}, queue: self.queue)
self.nextTypingDraftExpirationTimer?.start()
}
} else {
self.nextTypingDraftExpirationTimestamp = nil
if let nextTypingDraftExpirationTimer = self.nextTypingDraftExpirationTimer {
self.nextTypingDraftExpirationTimer = nil
nextTypingDraftExpirationTimer.invalidate()
}
}
}
private func processTypingDraftExpirations(expirationTimeout: Double) {
let timestamp = CFAbsoluteTimeGetCurrent()
var removedKeys: [PeerAndThreadId] = []
for (key, draft) in self.currentTypingDrafts {
if draft.addedAtTimestamp + expirationTimeout >= timestamp {
removedKeys.append(key)
}
}
if !removedKeys.isEmpty {
for key in removedKeys {
self.currentTypingDrafts.removeValue(forKey: key)
self.currentUpdatedTypingDrafts[key] = TypingDraftUpdate(value: nil)
}
}
}
func renderIntermediateMessage(_ message: IntermediateMessage) -> Message {
let renderedMessage = self.messageHistoryTable.renderMessage(message, peerTable: self.peerTable, threadIndexTable: self.messageHistoryThreadIndexTable, storyTable: self.storyTable)
@ -2427,7 +2597,7 @@ final class PostboxImpl {
}
private func beforeCommit(currentTransaction: Transaction) -> (updatedTransactionStateVersion: Int64?, updatedMasterClientId: Int64?) {
self.chatListTable.replay(historyOperationsByPeerId: self.currentOperationsByPeerId, updatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, updatedChatListInclusions: self.currentUpdatedChatListInclusions, messageHistoryTable: self.messageHistoryTable, peerChatInterfaceStateTable: self.peerChatInterfaceStateTable, operations: &self.currentChatListOperations)
self.chatListTable.replay(postbox: self, historyOperationsByPeerId: self.currentOperationsByPeerId, updatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, updatedPeerCachedData: self.currentUpdatedCachedPeerData, updatedChatListInclusions: self.currentUpdatedChatListInclusions, messageHistoryTable: self.messageHistoryTable, peerChatInterfaceStateTable: self.peerChatInterfaceStateTable, operations: &self.currentChatListOperations)
self.peerChatTopTaggedMessageIdsTable.replay(historyOperationsByPeerId: self.currentOperationsByPeerId)
@ -2534,7 +2704,8 @@ final class PostboxImpl {
storySubscriptionsEvents: self.currentStorySubscriptionsEvents,
storyItemsEvents: self.currentStoryItemsEvents,
currentStoryTopItemEvents: self.currentStoryTopItemEvents,
storyEvents: self.currentStoryEvents
storyEvents: self.currentStoryEvents,
updatedTypingDrafts: self.currentUpdatedTypingDrafts
)
var updatedTransactionState: Int64?
var updatedMasterClientId: Int64?
@ -2598,6 +2769,12 @@ final class PostboxImpl {
self.currentStoryTopItemEvents.removeAll()
self.currentStoryEvents.removeAll()
if !self.currentUpdatedTypingDrafts.isEmpty {
self.currentUpdatedTypingDrafts.removeAll()
self.restartTypingDraftExpirationTimerIfNeeded()
}
for table in self.tables {
table.beforeCommit()
}
@ -2759,7 +2936,7 @@ final class PostboxImpl {
let currentData = self.cachedPeerDataTable.get(peerId)
if let updatedData = update(peerId, currentData) {
self.cachedPeerDataTable.set(id: peerId, data: updatedData)
self.currentUpdatedCachedPeerData[peerId] = updatedData
self.currentUpdatedCachedPeerData[peerId] = (currentData, updatedData)
}
}
}

View file

@ -9,7 +9,7 @@ final class PostboxTransaction {
let currentUpdatedPeers: [PeerId: Peer]
let currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)]
let currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp]
let currentUpdatedCachedPeerData: [PeerId: CachedPeerData]
let currentUpdatedCachedPeerData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)]
let currentUpdatedPeerPresences: [PeerId: PeerPresence]
let currentUpdatedPeerChatListEmbeddedStates: Set<PeerId>
let currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState]
@ -56,6 +56,7 @@ final class PostboxTransaction {
let storyItemsEvents: [StoryItemsTable.Event]
let currentStoryTopItemEvents: [StoryTopItemsTable.Event]
let storyEvents: [StoryTable.Event]
let updatedTypingDrafts: [PeerAndThreadId: PostboxImpl.TypingDraftUpdate]
var isEmpty: Bool {
if currentUpdatedState != nil {
@ -223,6 +224,9 @@ final class PostboxTransaction {
if !self.storyEvents.isEmpty {
return false
}
if !self.updatedTypingDrafts.isEmpty {
return false
}
return true
}
@ -236,7 +240,7 @@ final class PostboxTransaction {
currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?,
PeerNotificationSettings)],
currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp],
currentUpdatedCachedPeerData: [PeerId: CachedPeerData],
currentUpdatedCachedPeerData: [PeerId: (previous: CachedPeerData?, updated: CachedPeerData)],
currentUpdatedPeerPresences: [PeerId: PeerPresence],
currentUpdatedPeerChatListEmbeddedStates: Set<PeerId>,
currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState],
@ -281,7 +285,8 @@ final class PostboxTransaction {
storySubscriptionsEvents: [StorySubscriptionsTable.Event],
storyItemsEvents: [StoryItemsTable.Event],
currentStoryTopItemEvents: [StoryTopItemsTable.Event],
storyEvents: [StoryTable.Event]
storyEvents: [StoryTable.Event],
updatedTypingDrafts: [PeerAndThreadId: PostboxImpl.TypingDraftUpdate]
) {
self.currentUpdatedState = currentUpdatedState
self.currentPeerHoleOperations = currentPeerHoleOperations
@ -337,5 +342,6 @@ final class PostboxTransaction {
self.storyItemsEvents = storyItemsEvents
self.currentStoryTopItemEvents = currentStoryTopItemEvents
self.storyEvents = storyEvents
self.updatedTypingDrafts = updatedTypingDrafts
}
}

View file

@ -60,7 +60,7 @@ public final class SeedConfiguration {
public let initializeChatListWithHole: (topLevel: ChatListHole?, groups: ChatListHole?)
public let messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]]
public let upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]]
public let messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]]
public let messageThreadHoles: (PeerId.Namespace, Int64?) -> [MessageId.Namespace]?
public let messageTagsWithSummary: MessageTags
public let messageTagsWithThreadSummary: MessageTags
public let existingGlobalMessageTags: GlobalMessageTags
@ -77,6 +77,7 @@ public final class SeedConfiguration {
public let decodeMessageThreadInfo: (CodableEntry) -> Message.AssociatedThreadInfo?
public let decodeAutoremoveTimeout: (CachedPeerData) -> Int32?
public let decodeDisplayPeerAsRegularChat: (CachedPeerData) -> Bool
public let decodeAssociatedChatListPeerId: (CachedPeerData) -> PeerId?
public let isPeerUpgradeMessage: (Message) -> Bool
public let automaticThreadIndexInfo: (PeerId, Int64) -> StoredMessageHistoryThreadInfo?
public let customTagsFromAttributes: ([MessageAttribute]) -> [MemoryBuffer]
@ -90,7 +91,7 @@ public final class SeedConfiguration {
),
messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]],
upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]],
messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]],
messageThreadHoles: @escaping (PeerId.Namespace, Int64?) -> [MessageId.Namespace]?,
existingMessageTags: MessageTags,
messageTagsWithSummary: MessageTags,
messageTagsWithThreadSummary: MessageTags,
@ -108,6 +109,7 @@ public final class SeedConfiguration {
decodeMessageThreadInfo: @escaping (CodableEntry) -> Message.AssociatedThreadInfo?,
decodeAutoremoveTimeout: @escaping (CachedPeerData) -> Int32?,
decodeDisplayPeerAsRegularChat: @escaping (CachedPeerData) -> Bool,
decodeAssociatedChatListPeerId: @escaping (CachedPeerData) -> PeerId?,
isPeerUpgradeMessage: @escaping (Message) -> Bool,
automaticThreadIndexInfo: @escaping (PeerId, Int64) -> StoredMessageHistoryThreadInfo?,
customTagsFromAttributes: @escaping ([MessageAttribute]) -> [MemoryBuffer],
@ -134,6 +136,7 @@ public final class SeedConfiguration {
self.decodeMessageThreadInfo = decodeMessageThreadInfo
self.decodeAutoremoveTimeout = decodeAutoremoveTimeout
self.decodeDisplayPeerAsRegularChat = decodeDisplayPeerAsRegularChat
self.decodeAssociatedChatListPeerId = decodeAssociatedChatListPeerId
self.isPeerUpgradeMessage = isPeerUpgradeMessage
self.automaticThreadIndexInfo = automaticThreadIndexInfo
self.customTagsFromAttributes = customTagsFromAttributes

View file

@ -93,6 +93,7 @@ enum AccountStateMutationOperation {
case AddSecretMessages([Api.EncryptedMessage])
case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32)
case AddPeerInputActivity(chatPeerId: PeerActivitySpace, peerId: PeerId?, activity: PeerInputActivity?)
case AddPeerLiveTypingDraftUpdate(peerAndThreadId: PeerAndThreadId, id: Int64, timestamp: Int32, peerId: PeerId, text: String, entities: [MessageTextEntity])
case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation)
case UpdatePinnedSavedItemIds(AccountStateUpdatePinnedItemIdsOperation)
case UpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool)
@ -592,6 +593,10 @@ struct AccountMutableState {
self.addOperation(.AddPeerInputActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity))
}
mutating func addPeerLiveTypingDraftUpdate(peerAndThreadId: PeerAndThreadId, id: Int64, timestamp: Int32, peerId: PeerId, text: String, entities: [MessageTextEntity]) {
self.addOperation(.AddPeerLiveTypingDraftUpdate(peerAndThreadId: peerAndThreadId, id: id, timestamp: timestamp, peerId: peerId, text: text, entities: entities))
}
mutating func addUpdatePinnedItemIds(groupId: PeerGroupId, operation: AccountStateUpdatePinnedItemIdsOperation) {
self.addOperation(.UpdatePinnedItemIds(groupId, operation))
}
@ -714,7 +719,7 @@ struct AccountMutableState {
mutating func addOperation(_ operation: AccountStateMutationOperation) {
switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException:
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException:
break
case let .AddMessages(messages, location):
for message in messages {

View file

@ -1071,7 +1071,7 @@ extension StoreMessage {
storeFlags.insert(.CanBeGroupedIntoFeed)
self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: groupingId, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: medias)
self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), customStableId: nil, globallyUniqueId: nil, groupingKey: groupingId, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: medias)
case .messageEmpty:
return nil
case let .messageService(flags, id, fromId, chatPeerId, savedPeerId, replyTo, date, action, reactions, ttlPeriod):
@ -1210,7 +1210,7 @@ extension StoreMessage {
storeFlags.insert(.ReactionsArePossible)
}
self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: nil, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: attributes, media: media)
self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), customStableId: nil, globallyUniqueId: nil, groupingKey: nil, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: attributes, media: media)
}
}
}

View file

@ -202,7 +202,11 @@ public enum CreateForumChannelTopicError {
}
func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title: String, iconColor: Int32, iconFileId: Int64?) -> Signal<Int64, CreateForumChannelTopicError> {
return account.postbox.transaction { transaction -> Peer? in
return _internal_createForumChannelTopic(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, title: title, iconColor: iconColor, iconFileId: iconFileId)
}
func _internal_createForumChannelTopic(postbox: Postbox, network: Network, stateManager: AccountStateManager, accountPeerId: PeerId, peerId: PeerId, title: String, iconColor: Int32, iconFileId: Int64?) -> Signal<Int64, CreateForumChannelTopicError> {
return postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
}
|> castError(CreateForumChannelTopicError.self)
@ -218,7 +222,7 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title:
flags |= (1 << 3)
}
flags |= (1 << 0)
return account.network.request(Api.functions.channels.createForumTopic(
return network.request(Api.functions.channels.createForumTopic(
flags: flags,
channel: inputChannel,
title: title,
@ -231,14 +235,14 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title:
return .generic
}
|> mapToSignal { result -> Signal<Int64, CreateForumChannelTopicError> in
account.stateManager.addUpdates(result)
stateManager.addUpdates(result)
var topicId: Int64?
topicId = nil
for update in result.allUpdates {
switch update {
case let .updateNewChannelMessage(message, _, _):
if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) {
if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peer.isForum) {
if case let .Id(id) = message.id {
topicId = Int64(id.id)
}
@ -249,12 +253,12 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title:
}
if let topicId {
return account.postbox.transaction { transaction -> Void in
return postbox.transaction { transaction -> Void in
transaction.removeHole(peerId: peerId, threadId: topicId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: 1 ... (Int32.max - 1))
}
|> castError(CreateForumChannelTopicError.self)
|> mapToSignal { _ -> Signal<Int64, CreateForumChannelTopicError> in
return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)])
return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: .network(network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)])
|> castError(CreateForumChannelTopicError.self)
|> map { _ -> Int64 in
return topicId

View file

@ -828,7 +828,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
threadId = 1
}
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList))
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, customStableId: nil, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList))
case let .forward(source, threadId, grouping, requestedAttributes, _):
let sourceMessage = transaction.getMessage(source)
if let sourceMessage = sourceMessage, let author = sourceMessage.author ?? sourceMessage.peers[sourceMessage.id.peerId] {
@ -1073,7 +1073,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
threadId = 1
}
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: augmentedMediaList))
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, customStableId: nil, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: augmentedMediaList))
}
}
}

View file

@ -584,7 +584,7 @@ if "".isEmpty {
}
}
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: updatedAttributes, media: updatedMedia))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: updatedAttributes, media: updatedMedia))
})
}
return .done(media)
@ -1035,7 +1035,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
}
}
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: updatedAttributes, media: updatedMedia))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: updatedAttributes, media: updatedMedia))
})
}
return .done(media)

View file

@ -128,7 +128,7 @@ private func generatePeerMediaMessage(network: Network, accountPeerId: EnginePee
media.append(TelegramMediaAction(action: .setChatWallpaper(wallpaper: wallpaper, forBoth: forBoth)))
}
return StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: accountPeerId, text: "", attributes: attributes, media: media)
return StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: accountPeerId, text: "", attributes: attributes, media: media)
}
private final class PendingPeerMediaUploadContext {

View file

@ -358,7 +358,7 @@ func _internal_requestEditLiveLocation(postbox: Postbox, network: Network, state
}
var updatedLocalTags = currentMessage.localTags
updatedLocalTags.remove(.OutgoingLiveLocation)
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: updatedLocalTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: updatedLocalTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
} else {

View file

@ -1193,7 +1193,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
}
}
let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: messageText, attributes: attributes, media: medias)
let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, customStableId: nil, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: messageText, attributes: attributes, media: medias)
updatedState.addMessages([message], location: .UpperHistoryBlock)
}
}
@ -1470,15 +1470,22 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
let threadId = topMsgId.flatMap { Int64($0) }
let activity = PeerInputActivity(apiType: type, peerId: nil, timestamp: date)
var category: PeerActivitySpace.Category = .global
if case .speakingInGroupCall = activity {
category = .voiceChat
} else if let threadId = threadId {
category = .thread(threadId)
if case let .sendMessageTextDraftAction(randomId, text) = type {
switch text {
case let .textWithEntities(text, entities):
updatedState.addPeerLiveTypingDraftUpdate(peerAndThreadId: PeerAndThreadId(peerId: channelPeerId, threadId: threadId), id: randomId, timestamp: date, peerId: userId.peerId, text: text, entities: messageTextEntitiesFromApiEntities(entities))
}
} else {
let activity = PeerInputActivity(apiType: type, peerId: nil, timestamp: date)
var category: PeerActivitySpace.Category = .global
if case .speakingInGroupCall = activity {
category = .voiceChat
} else if let threadId = threadId {
category = .thread(threadId)
}
updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: channelPeerId, category: category), peerId: userId.peerId, activity: activity)
}
updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: channelPeerId, category: category), peerId: userId.peerId, activity: activity)
}
case let .updateEncryptedChatTyping(chatId):
if let date = updatesDate, date + 60 > serverTime {
@ -3583,7 +3590,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
for operation in operations {
switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException:
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException:
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
}
@ -3767,6 +3774,29 @@ func replayFinalState(
var addedOperationIncomingMessageIds: [MessageId] = []
var addedConferenceInvitationMessagesIds: [MessageId] = []
enum LiveTypingDraftUpdate {
struct Update {
var id: Int64
var authorId: PeerId
var timestamp: Int32
var text: String
var entities: [MessageTextEntity]
init(id: Int64, authorId: PeerId, timestamp: Int32, text: String, entities: [MessageTextEntity]) {
self.id = id
self.authorId = authorId
self.timestamp = timestamp
self.text = text
self.entities = entities
}
}
case update(Update)
case cancel
}
var liveTypingDraftUpdates: [PeerAndThreadId: LiveTypingDraftUpdate] = [:]
for operation in finalState.state.operations {
switch operation {
@ -3965,6 +3995,24 @@ func replayFinalState(
}
}
}
var messages = messages
if case .UpperHistoryBlock = location {
for i in 0 ..< messages.count {
let message = messages[i]
let chatPeerId = message.id.peerId
let key = PeerAndThreadId(peerId: chatPeerId, threadId: message.threadId)
if liveTypingDraftUpdates[key] != nil {
liveTypingDraftUpdates[key] = .cancel
} else if let currentDraft = transaction.getCurrentTypingDraft(location: key) {
liveTypingDraftUpdates[key] = .cancel
messages[i] = messages[i].withUpdatedCustomStableId(currentDraft.stableId)
}
}
}
let _ = transaction.addMessages(messages, location: location)
if case .UpperHistoryBlock = location {
for message in messages {
@ -3982,6 +4030,7 @@ func replayFinalState(
} else {
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, category: .thread(threadId))]![authorId] = activityValue
}
}
}
@ -4317,7 +4366,7 @@ func replayFinalState(
return .skip
}
attributes[j] = ReplyThreadMessageAttribute(count: attribute.count, latestUsers: attribute.latestUsers, commentsPeerId: attribute.commentsPeerId, maxMessageId: attribute.maxMessageId, maxReadMessageId: readMaxId)
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
}
}
return .skip
@ -4578,7 +4627,7 @@ func replayFinalState(
return .skip
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
case let .MergePeerPresences(statuses, explicit):
@ -4622,6 +4671,14 @@ func replayFinalState(
} else if chatPeerId.peerId.namespace == Namespaces.Peer.SecretChat {
updatedSecretChatTypingActivities.insert(chatPeerId.peerId)
}
case let .AddPeerLiveTypingDraftUpdate(peerAndThreadId, id, timestamp, authorId, text, entities):
liveTypingDraftUpdates[peerAndThreadId] = .update(LiveTypingDraftUpdate.Update(
id: id,
authorId: authorId,
timestamp: timestamp,
text: text,
entities: entities
))
case let .UpdatePinnedItemIds(groupId, pinnedOperation):
switch pinnedOperation {
case let .pin(itemId):
@ -4733,7 +4790,7 @@ func replayFinalState(
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: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
case let .UpdateMessageForwardsCount(id, count):
transaction.updateMessage(id, update: { currentMessage in
@ -4748,7 +4805,7 @@ func replayFinalState(
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: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
case let .UpdateInstalledStickerPacks(operation):
stickerPackOperations.append(operation)
@ -4932,7 +4989,7 @@ func replayFinalState(
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
case .UpdateAttachMenuBots:
syncAttachMenuBots = true
@ -4957,6 +5014,7 @@ func replayFinalState(
return .update(StoreMessage(
id: currentMessage.id,
customStableId: nil,
globallyUniqueId: currentMessage.globallyUniqueId,
groupingKey: currentMessage.groupingKey,
threadId: currentMessage.threadId,
@ -5000,7 +5058,7 @@ 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: currentMessage.attributes, media: media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: media))
})
case let .ResetForumTopic(topicId, data, pts):
if finalState.state.resetForumTopicLists[topicId.peerId] == nil {
@ -5691,6 +5749,26 @@ func replayFinalState(
_internal_setStarsReactionDefaultPrivacy(privacy: updatedStarsReactionsDefaultPrivacy, transaction: transaction)
}
if !liveTypingDraftUpdates.isEmpty {
transaction.combineTypingDrafts(locations: Set(liveTypingDraftUpdates.keys), update: { key, current in
guard let update = liveTypingDraftUpdates[key] else {
return current
}
switch update {
case let .update(update):
return (
update.id,
update.authorId,
update.timestamp,
update.text,
[TextEntitiesMessageAttribute(entities: update.entities)]
)
case .cancel:
return nil
}
})
}
return AccountReplayedFinalState(
state: finalState,
addedIncomingMessageIds: addedIncomingMessageIds,

View file

@ -450,7 +450,7 @@ public final class AccountViewTracker {
break
}
}
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: currentMessage.attributes, media: media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: media))
})
}
}
@ -798,7 +798,7 @@ public final class AccountViewTracker {
if !foundReplies, let repliesCount = repliesCount {
attributes.append(ReplyThreadMessageAttribute(count: repliesCount, latestUsers: recentRepliersPeerIds ?? [], commentsPeerId: commentsChannelId, maxMessageId: repliesMaxId, maxReadMessageId: repliesReadMaxId))
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -898,7 +898,7 @@ public final class AccountViewTracker {
} 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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
default:
break
@ -1595,7 +1595,7 @@ public final class AccountViewTracker {
}
var tags = currentMessage.tags
tags.remove(.unseenPersonalMessage)
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
@ -1645,7 +1645,7 @@ public final class AccountViewTracker {
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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())
@ -1678,7 +1678,7 @@ public final class AccountViewTracker {
}
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: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
@ -1719,7 +1719,7 @@ public final class AccountViewTracker {
}
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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: 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 {

View file

@ -316,7 +316,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
}
}
let updatedMessageValue = StoreMessage(id: updatedId, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)
let updatedMessageValue = StoreMessage(id: updatedId, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)
updatedMessage = updatedMessageValue
return .update(updatedMessageValue)
@ -546,7 +546,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage
let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities, isPinned: currentMessage.tags.contains(.pinned))
return .update(StoreMessage(id: updatedId, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media))
return .update(StoreMessage(id: updatedId, customStableId: nil, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media))
})
}

View file

@ -883,7 +883,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran
let updatedFlags = StoreMessageFlags(currentMessage.flags)
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
}
})
@ -923,7 +923,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran
default:
break
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -956,7 +956,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran
default:
break
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
} else {
_internal_deleteMessages(transaction: transaction, mediaBox: postbox.mediaBox, ids: [id])
@ -991,7 +991,7 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran
default:
break
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -1124,7 +1124,7 @@ private func validateReplyThreadBatch(postbox: Postbox, network: Network, transa
let updatedFlags = StoreMessageFlags(currentMessage.flags)
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
}
})

View file

@ -111,7 +111,7 @@ func managedAutoremoveMessageOperations(network: Network, postbox: Postbox, isRe
break
}
}
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: updatedAttributes, media: updatedMedia))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: updatedAttributes, media: updatedMedia))
})
}
} else {

View file

@ -276,7 +276,7 @@ private func synchronizeConsumeMessageContents(transaction: Transaction, postbox
}
var updatedTags = currentMessage.tags
updatedTags.remove(.unseenPersonalMessage)
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -302,7 +302,7 @@ private func synchronizeConsumeMessageContents(transaction: Transaction, postbox
}
var updatedTags = currentMessage.tags
updatedTags.remove(.unseenPersonalMessage)
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -344,7 +344,7 @@ private func synchronizeReadMessageReactions(transaction: Transaction, postbox:
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -371,7 +371,7 @@ private func synchronizeReadMessageReactions(transaction: Transaction, postbox:
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}

View file

@ -1822,7 +1822,7 @@ private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Pos
}
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia))
})
maybeReadSecretOutgoingMessage(transaction: transaction, index: MessageIndex(id: message.id, timestamp: timestamp))
@ -1982,6 +1982,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po
let storedMessage = StoreMessage(
peerId: peerId,
namespace: Namespaces.Message.Local,
customStableId: nil,
globallyUniqueId: globallyUniqueId,
groupingKey: nil,
threadId: nil,
@ -2062,7 +2063,7 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId
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)
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
if let resultTimestamp = resultTimestamp {

View file

@ -223,7 +223,7 @@ func markUnseenPersonalMessage(transaction: Transaction, id: MessageId, addSynch
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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 {
@ -348,7 +348,7 @@ func markUnseenReactionMessage(transaction: Transaction, id: MessageId, addSynch
}
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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
if addSynchronizeAction {

View file

@ -165,7 +165,7 @@ public func updateMessageReactionsInteractively(account: Account, messageIds: [M
attributes.append(PendingReactionsMessageAttribute(accountPeerId: account.peerId, reactions: mappedReactions, isLarge: isLarge, storeAsRecentlyUsed: storeAsRecentlyUsed, isTags: currentMessage.areReactionsTags(accountPeerId: account.peerId)))
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -217,7 +217,7 @@ func _internal_sendStarsReactionsInteractively(account: Account, messageId: Mess
resolvedPrivacyValue = resolvedPrivacy
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
return resolvedPrivacyValue
@ -240,7 +240,7 @@ func cancelPendingSendStarsReactionInteractively(account: Account, messageId: Me
}
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
|> ignoreValues
@ -281,7 +281,7 @@ func _internal_updateStarsReactionPrivacy(account: Account, messageId: MessageId
attributes[j] = ReactionsMessageAttribute(canViewList: attribute.canViewList, isTags: attribute.isTags, reactions: attribute.reactions, recentPeers: attribute.recentPeers, topPeers: updatedTopPeers)
}
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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 privacyPeerId: PeerId?
@ -392,7 +392,7 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st
if let reactions = reactions {
attributes.append(reactions)
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
stateManager.addUpdates(result)
}
@ -483,7 +483,7 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM
if let reactions {
attributes.append(reactions)
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
stateManager.addUpdates(result)
}
@ -706,7 +706,7 @@ private func synchronizeMessageReactions(transaction: Transaction, postbox: Post
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: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
|> ignoreValues
@ -730,7 +730,7 @@ private func synchronizeMessageStarsReactions(transaction: Transaction, postbox:
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: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
|> ignoreValues

View file

@ -200,126 +200,124 @@ private final class PeerGlobalInputActivityContext {
}
final class PeerInputActivityManager {
private let queue = Queue()
private var nextEpisodeId: Int32 = 0
private var nextUpdateId: Int32 = 0
private var contexts: [PeerActivitySpace: PeerInputActivityContext] = [:]
private var globalContext: PeerGlobalInputActivityContext?
func activities(peerId: PeerActivitySpace) -> Signal<[(PeerId, PeerInputActivityRecord)], NoError> {
let queue = self.queue
return Signal { [weak self] subscriber in
let disposable = MetaDisposable()
queue.async {
if let strongSelf = self {
let context: PeerInputActivityContext
if let currentContext = strongSelf.contexts[peerId] {
context = currentContext
} else {
context = PeerInputActivityContext(queue: queue, notifyEmpty: {
if let strongSelf = self {
strongSelf.contexts.removeValue(forKey: peerId)
if let globalContext = strongSelf.globalContext {
let activities = strongSelf.collectActivities()
globalContext.notify(activities)
}
}
}, notifyUpdated: {
if let strongSelf = self, let globalContext = strongSelf.globalContext {
let activities = strongSelf.collectActivities()
globalContext.notify(activities)
}
})
strongSelf.contexts[peerId] = context
}
let index = context.addSubscriber({ next in
subscriber.putNext(next)
})
subscriber.putNext(context.topActivities())
disposable.set(ActionDisposable {
queue.async {
if let strongSelf = self {
if let currentContext = strongSelf.contexts[peerId] {
currentContext.removeSubscriber(index)
if currentContext.isEmpty() {
strongSelf.contexts.removeValue(forKey: peerId)
}
}
}
}
})
}
}
return disposable
}
}
private func collectActivities() -> [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] {
assert(self.queue.isCurrent())
private final class Impl {
let queue: Queue
var dict: [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] = [:]
for (chatPeerId, context) in self.contexts {
dict[chatPeerId] = context.topActivities()
var nextEpisodeId: Int32 = 0
var nextUpdateId: Int32 = 0
var contexts: [PeerActivitySpace: PeerInputActivityContext] = [:]
var globalContext: PeerGlobalInputActivityContext?
init(queue: Queue) {
self.queue = queue
}
return dict
}
func allActivities() -> Signal<[PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]], NoError> {
let queue = self.queue
return Signal { [weak self] subscriber in
let disposable = MetaDisposable()
queue.async {
if let strongSelf = self {
let context: PeerGlobalInputActivityContext
if let current = strongSelf.globalContext {
context = current
} else {
context = PeerGlobalInputActivityContext()
strongSelf.globalContext = context
func activities(peerId: PeerActivitySpace, onNext: @escaping ([(PeerId, PeerInputActivityRecord)]) -> Void) -> Disposable {
let context: PeerInputActivityContext
if let currentContext = self.contexts[peerId] {
context = currentContext
} else {
context = PeerInputActivityContext(queue: queue, notifyEmpty: { [weak self] in
guard let self else {
return
}
let index = context.addSubscriber({ next in
subscriber.putNext(next)
})
subscriber.putNext(strongSelf.collectActivities())
self.contexts.removeValue(forKey: peerId)
disposable.set(ActionDisposable {
queue.async {
if let strongSelf = self {
if let currentContext = strongSelf.globalContext {
currentContext.removeSubscriber(index)
if currentContext.isEmpty {
strongSelf.globalContext = nil
}
}
}
if let globalContext = self.globalContext {
let activities = self.collectActivities()
globalContext.notify(activities)
}
}, notifyUpdated: { [weak self] in
guard let self else {
return
}
if let globalContext = self.globalContext {
let activities = self.collectActivities()
globalContext.notify(activities)
}
})
self.contexts[peerId] = context
}
let index = context.addSubscriber({ next in
onNext(next)
})
onNext(context.topActivities())
let queue = self.queue
return ActionDisposable { [weak self] in
queue.async {
guard let self else {
return
}
if let currentContext = self.contexts[peerId] {
currentContext.removeSubscriber(index)
if currentContext.isEmpty() {
self.contexts.removeValue(forKey: peerId)
}
})
}
}
}
return disposable
}
}
func addActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
self.queue.async {
func allActivities(onNext: @escaping ([PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]]) -> Void) -> Disposable {
let context: PeerGlobalInputActivityContext
if let current = self.globalContext {
context = current
} else {
context = PeerGlobalInputActivityContext()
self.globalContext = context
}
let index = context.addSubscriber({ next in
onNext(next)
})
onNext(self.collectActivities())
let queue = self.queue
return ActionDisposable { [weak self] in
queue.async {
guard let self else {
return
}
if let currentContext = self.globalContext {
currentContext.removeSubscriber(index)
if currentContext.isEmpty {
self.globalContext = nil
}
}
}
}
}
private func collectActivities() -> [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] {
assert(self.queue.isCurrent())
var dict: [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] = [:]
for (chatPeerId, context) in self.contexts {
dict[chatPeerId] = context.topActivities()
}
return dict
}
func addActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32?) {
let context: PeerInputActivityContext
if let currentContext = self.contexts[chatPeerId] {
context = currentContext
} else {
context = PeerInputActivityContext(queue: self.queue, notifyEmpty: { [weak self] in
if let strongSelf = self {
strongSelf.contexts.removeValue(forKey: chatPeerId)
if let globalContext = strongSelf.globalContext {
let activities = strongSelf.collectActivities()
globalContext.notify(activities)
}
guard let self else {
return
}
self.contexts.removeValue(forKey: chatPeerId)
if let globalContext = self.globalContext {
let activities = self.collectActivities()
globalContext.notify(activities)
}
}, notifyUpdated: { [weak self] in
if let strongSelf = self, let globalContext = strongSelf.globalContext {
let activities = strongSelf.collectActivities()
guard let self else {
return
}
if let globalContext = self.globalContext {
let activities = self.collectActivities()
globalContext.notify(activities)
}
})
@ -347,10 +345,8 @@ final class PeerInputActivityManager {
globalContext.notify(activities)
}
}
}
func removeActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
self.queue.async {
func removeActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32?) {
if let context = self.contexts[chatPeerId] {
context.removeActivity(peerId: peerId, activity: activity, episodeId: episodeId)
@ -360,10 +356,8 @@ final class PeerInputActivityManager {
}
}
}
}
func removeAllActivities(chatPeerId: PeerActivitySpace, peerId: PeerId) {
self.queue.async {
func removeAllActivities(chatPeerId: PeerActivitySpace, peerId: PeerId) {
if let currentContext = self.contexts[chatPeerId] {
currentContext.removeAllActivities(peerId: peerId)
@ -373,23 +367,17 @@ final class PeerInputActivityManager {
}
}
}
}
func transaction(_ f: @escaping (PeerInputActivityManager) -> Void) {
self.queue.async {
f(self)
}
}
func acquireActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity) -> Disposable {
let disposable = MetaDisposable()
let queue = self.queue
queue.async {
func acquireActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity) -> Disposable {
let queue = self.queue
let episodeId = self.nextEpisodeId
self.nextEpisodeId += 1
let update: () -> Void = { [weak self] in
self?.addActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId)
guard let self else {
return
}
self.addActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId)
}
let timeout: Double
@ -406,16 +394,69 @@ final class PeerInputActivityManager {
timer.start()
update()
disposable.set(ActionDisposable { [weak self] in
return ActionDisposable { [weak self] in
queue.async {
timer.invalidate()
guard let strongSelf = self else {
guard let self else {
return
}
strongSelf.removeActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId)
self.removeActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId)
}
})
}
}
}
private let queue = Queue()
private let impl: QueueLocalObject<Impl>
init() {
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return Impl(queue: queue)
})
}
func activities(peerId: PeerActivitySpace) -> Signal<[(PeerId, PeerInputActivityRecord)], NoError> {
return self.impl.signalWith { impl, subscriber in
return impl.activities(peerId: peerId, onNext: subscriber.putNext)
}
}
func allActivities() -> Signal<[PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]], NoError> {
return self.impl.signalWith { impl, subscriber in
return impl.allActivities(onNext: subscriber.putNext)
}
}
func addActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
self.impl.with { impl in
impl.addActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId)
}
}
func removeActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity, episodeId: Int32? = nil) {
self.impl.with { impl in
impl.removeActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity, episodeId: episodeId)
}
}
func removeAllActivities(chatPeerId: PeerActivitySpace, peerId: PeerId) {
self.impl.with { impl in
impl.removeAllActivities(chatPeerId: chatPeerId, peerId: peerId)
}
}
func transaction(_ f: @escaping (PeerInputActivityManager) -> Void) {
self.queue.async {
f(self)
}
}
func acquireActivity(chatPeerId: PeerActivitySpace, peerId: PeerId, activity: PeerInputActivity) -> Disposable {
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.acquireActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity))
}
return disposable
}

View file

@ -32,21 +32,24 @@ private enum PendingMessageState {
case uploading(groupId: Int64?)
case waitingToBeSent(groupId: Int64?, content: PendingMessageUploadedContentAndReuploadInfo)
case sending(groupId: Int64?)
case waitingForNewTopic(message: Message)
var groupId: Int64? {
switch self {
case .none:
return nil
case let .collectingInfo(message):
return message.groupingKey
case let .waitingForUploadToStart(groupId, _):
return groupId
case let .uploading(groupId):
return groupId
case let .waitingToBeSent(groupId, _):
return groupId
case let .sending(groupId):
return groupId
case .none:
return nil
case let .collectingInfo(message):
return message.groupingKey
case let .waitingForUploadToStart(groupId, _):
return groupId
case let .uploading(groupId):
return groupId
case let .waitingToBeSent(groupId, _):
return groupId
case let .sending(groupId):
return groupId
case let .waitingForNewTopic(message):
return message.groupingKey
}
}
}
@ -115,6 +118,20 @@ public struct PeerPendingMessageDelivered {
private final class PeerPendingMessagesSummaryContext {
var messageDeliveredSubscribers = Bag<([PeerPendingMessageDelivered]) -> Void>()
var messageFailedSubscribers = Bag<(PendingMessageFailureReason) -> Void>()
var newTopicEvents = Bag<(PendingMessageManager.NewTopicEvent) -> Void>()
var isEmpty: Bool {
if !self.messageDeliveredSubscribers.isEmpty {
return false
}
if !self.messageFailedSubscribers.isEmpty {
return false
}
if !self.newTopicEvents.isEmpty {
return false
}
return true
}
}
private enum PendingMessageResult {
@ -158,7 +175,7 @@ private func failMessages(postbox: Postbox, ids: [MessageId]) -> Signal<Void, No
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)
}
return .update(StoreMessage(id: id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
}
@ -186,6 +203,11 @@ private final class CorrelationIdToSentMessageId {
}
public final class PendingMessageManager {
public enum NewTopicEvent {
case willMove(fromThreadId: Int64, toThreadId: Int64)
case didMove(fromThreadId: Int64, toThreadId: Int64)
}
private let network: Network
private let postbox: Postbox
private let accountPeerId: PeerId
@ -206,6 +228,8 @@ public final class PendingMessageManager {
private var pendingMessageIds = Set<MessageId>()
private let beginSendingMessagesDisposables = DisposableSet()
private var newTopicDisposables: [PeerId: Disposable] = [:]
private var peerSummaryContexts: [PeerId: PeerPendingMessagesSummaryContext] = [:]
var transformOutgoingMessageMedia: TransformOutgoingMessageMedia?
@ -226,6 +250,9 @@ public final class PendingMessageManager {
deinit {
self.beginSendingMessagesDisposables.dispose()
for (_, disposable) in self.newTopicDisposables {
disposable.dispose()
}
}
func updatePendingMessageIds(_ messageIds: Set<MessageId>) {
@ -422,9 +449,7 @@ public final class PendingMessageManager {
assert(strongSelf.queue.isCurrent())
Logger.shared.log("PendingMessageManager", "begin sending, continued: \(ids)")
Logger.shared.log("PendingMessageManager", "beginSendingMessages messages.count: \(messages.count)")
for message in messages.filter({ !$0.flags.contains(.Sending) }).sorted(by: { $0.id < $1.id }) {
guard let messageContext = strongSelf.messageContexts[message.id] else {
@ -435,14 +460,106 @@ public final class PendingMessageManager {
messageContext.activityType = uploadActivityTypeForMessage(message)
}
messageContext.threadId = message.threadId
strongSelf.collectUploadingInfo(messageContext: messageContext, message: message)
if messageContext.threadId == Message.newTopicThreadId {
strongSelf.createNewTopic(messageContext: messageContext, message: message)
} else {
strongSelf.collectUploadingInfo(messageContext: messageContext, message: message)
}
}
var messagesToUpload: [(PendingMessageContext, Message, PendingMessageUploadedContentType, Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>)] = []
var messagesToForward: [PeerIdAndNamespace: [(PendingMessageContext, Message, ForwardSourceInfoAttribute)]] = [:]
Logger.shared.log("PendingMessageManager", "beginSendingMessages messageContexts.count: \(strongSelf.messageContexts.count)")
for (messageContext, _) in strongSelf.messageContexts.values.compactMap({ messageContext -> (PendingMessageContext, Message)? in
if case let .waitingForNewTopic(message) = messageContext.state {
return (messageContext, message)
} else {
return nil
}
}).sorted(by: { lhs, rhs in
return lhs.1.index < rhs.1.index
}) {
if case let .waitingForNewTopic(message) = messageContext.state {
let messagePeerId = message.id.peerId
if strongSelf.newTopicDisposables[messagePeerId] == nil {
let disposable = MetaDisposable()
strongSelf.newTopicDisposables[messagePeerId] = disposable
disposable.set(_internal_createForumChannelTopic(
postbox: strongSelf.postbox,
network: strongSelf.network,
stateManager: strongSelf.stateManager,
accountPeerId: strongSelf.accountPeerId,
peerId: message.id.peerId,
title: "Topic #\(message.stableId)",
iconColor: 0,
iconFileId: nil
).startStrict(next: { [weak strongSelf] topicId in
guard let strongSelf else {
return
}
var moveMessageIds: [MessageId] = []
for (_, messageContext) in strongSelf.messageContexts {
if case let .waitingForNewTopic(message) = messageContext.state {
if message.id.peerId == messagePeerId {
moveMessageIds.append(message.id)
}
}
}
if !moveMessageIds.isEmpty {
if let context = strongSelf.peerSummaryContexts[messagePeerId] {
for subscriber in context.newTopicEvents.copyItems() {
subscriber(.willMove(fromThreadId: Message.newTopicThreadId, toThreadId: topicId))
}
}
let _ = (strongSelf.postbox.transaction { transaction -> [Message] in
var result: [Message] = []
for id in moveMessageIds {
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)
}
return .update(StoreMessage(id: id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: topicId, 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: currentMessage.attributes, media: currentMessage.media))
})
if let message = transaction.getMessage(id) {
result.append(message)
}
}
return result
}
|> deliverOn(strongSelf.queue)).startStandalone(next: { [weak strongSelf] messages in
guard let strongSelf else {
return
}
for message in messages {
if let context = strongSelf.messageContexts[message.id] {
if case .waitingForNewTopic = context.state {
context.state = .collectingInfo(message: message)
}
}
}
strongSelf.newTopicDisposables[messagePeerId]?.dispose()
strongSelf.newTopicDisposables[messagePeerId] = nil
strongSelf.beginSendingMessages(messages.map(\.id))
if let context = strongSelf.peerSummaryContexts[messagePeerId] {
for subscriber in context.newTopicEvents.copyItems() {
subscriber(.didMove(fromThreadId: Message.newTopicThreadId, toThreadId: topicId))
}
}
})
}
}, error: { _ in
//TODO:release handle errors
}))
}
}
}
for (messageContext, _) in strongSelf.messageContexts.values.compactMap({ messageContext -> (PendingMessageContext, Message)? in
if case let .collectingInfo(message) = messageContext.state {
@ -486,7 +603,6 @@ public final class PendingMessageManager {
}
Logger.shared.log("PendingMessageManager", "beginSendingMessages messagesToUpload.count: \(messagesToUpload.count)")
for (messageContext, message, type, contentUploadSignal) in messagesToUpload {
if let paidStarsAttribute = message.paidStarsAttribute, paidStarsAttribute.postponeSending {
@ -596,28 +712,32 @@ public final class PendingMessageManager {
loop: for (id, context) in self.messageContexts {
switch context.state {
case .none:
continue loop
case let .collectingInfo(message):
if message.groupingKey == groupId {
return nil
}
case let .waitingForUploadToStart(contextGroupId, _):
if contextGroupId == groupId {
return nil
}
case let .uploading(contextGroupId):
if contextGroupId == groupId {
return nil
}
case let .waitingToBeSent(contextGroupId, content):
if contextGroupId == groupId {
result.append((context, id, content))
}
case let .sending(contextGroupId):
if contextGroupId == groupId {
return nil
}
case .none:
continue loop
case let .collectingInfo(message):
if message.groupingKey == groupId {
return nil
}
case let .waitingForUploadToStart(contextGroupId, _):
if contextGroupId == groupId {
return nil
}
case let .uploading(contextGroupId):
if contextGroupId == groupId {
return nil
}
case let .waitingToBeSent(contextGroupId, content):
if contextGroupId == groupId {
result.append((context, id, content))
}
case let .sending(contextGroupId):
if contextGroupId == groupId {
return nil
}
case let .waitingForNewTopic(message):
if message.groupingKey == groupId {
return nil
}
}
}
@ -665,6 +785,10 @@ public final class PendingMessageManager {
}))
}
private func createNewTopic(messageContext: PendingMessageContext, message: Message) {
messageContext.state = .waitingForNewTopic(message: message)
}
private func collectUploadingInfo(messageContext: PendingMessageContext, message: Message) {
messageContext.state = .collectingInfo(message: message)
}
@ -724,7 +848,7 @@ public final class PendingMessageManager {
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)
}
return .update(StoreMessage(id: id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
return modify
@ -1307,7 +1431,7 @@ public final class PendingMessageManager {
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)
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
} else {
let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: message.id.peerId, operation: .sendMessage(layer: layer, id: message.id, file: secretFile), state: state)
@ -1323,7 +1447,7 @@ public final class PendingMessageManager {
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)
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: flags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
} else {
@ -1332,7 +1456,7 @@ public final class PendingMessageManager {
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)
}
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
}
@ -1850,7 +1974,7 @@ public final class PendingMessageManager {
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)
}
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}).start()
}
@ -1864,7 +1988,7 @@ public final class PendingMessageManager {
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)
}
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: message.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: [.Failed], tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media))
})
}
}
@ -1892,7 +2016,7 @@ public final class PendingMessageManager {
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)
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -2030,7 +2154,7 @@ public final class PendingMessageManager {
self.queue.async {
if let current = self.peerSummaryContexts[peerId] {
current.messageDeliveredSubscribers.remove(index)
if current.messageDeliveredSubscribers.isEmpty {
if current.isEmpty {
self.peerSummaryContexts.removeValue(forKey: peerId)
}
}
@ -2063,7 +2187,40 @@ public final class PendingMessageManager {
self.queue.async {
if let current = self.peerSummaryContexts[peerId] {
current.messageFailedSubscribers.remove(index)
if current.messageFailedSubscribers.isEmpty {
if current.isEmpty {
self.peerSummaryContexts.removeValue(forKey: peerId)
}
}
}
})
}
return disposable
}
}
public func newTopicEvents(peerId: PeerId) -> Signal<NewTopicEvent, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.queue.async {
let summaryContext: PeerPendingMessagesSummaryContext
if let current = self.peerSummaryContexts[peerId] {
summaryContext = current
} else {
summaryContext = PeerPendingMessagesSummaryContext()
self.peerSummaryContexts[peerId] = summaryContext
}
let index = summaryContext.newTopicEvents.add({ reason in
subscriber.putNext(reason)
})
disposable.set(ActionDisposable {
self.queue.async {
if let current = self.peerSummaryContexts[peerId] {
current.newTopicEvents.remove(index)
if current.isEmpty {
self.peerSummaryContexts.removeValue(forKey: peerId)
}
}

View file

@ -567,7 +567,7 @@ extension StoreMessage {
convenience init?(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi8.DecryptedMessage, file: SecretChatFileReference?) {
switch apiMessage {
case let .decryptedMessage(randomId, _, message, _):
self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: message, attributes: [], media: [])
self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: message, attributes: [], media: [])
case let .decryptedMessageService(randomId, _, action):
switch action {
case .decryptedMessageActionDeleteMessages:
@ -579,9 +579,9 @@ extension StoreMessage {
case .decryptedMessageActionReadMessages:
return nil
case .decryptedMessageActionScreenshotMessages:
self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)])
self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)])
case let .decryptedMessageActionSetMessageTTL(ttlSeconds):
self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))])
self.init(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))])
}
}
}
@ -894,7 +894,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
case let .decryptedMessageService(randomId, action):
switch action {
case .decryptedMessageActionDeleteMessages:
@ -906,9 +906,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case .decryptedMessageActionReadMessages:
return nil
case .decryptedMessageActionScreenshotMessages:
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
case let .decryptedMessageActionSetMessageTTL(ttlSeconds):
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
case .decryptedMessageActionResend:
return nil
case .decryptedMessageActionRequestKey:
@ -1126,7 +1126,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
case let .decryptedMessageService(randomId, action):
switch action {
case .decryptedMessageActionDeleteMessages:
@ -1138,9 +1138,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case .decryptedMessageActionReadMessages:
return nil
case .decryptedMessageActionScreenshotMessages:
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
case let .decryptedMessageActionSetMessageTTL(ttlSeconds):
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
case .decryptedMessageActionResend:
return nil
case .decryptedMessageActionRequestKey:
@ -1405,7 +1405,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
case let .decryptedMessageService(randomId, action):
switch action {
case .decryptedMessageActionDeleteMessages:
@ -1417,9 +1417,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case .decryptedMessageActionReadMessages:
return nil
case .decryptedMessageActionScreenshotMessages:
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
case let .decryptedMessageActionSetMessageTTL(ttlSeconds):
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
case .decryptedMessageActionResend:
return nil
case .decryptedMessageActionRequestKey:
@ -1606,7 +1606,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources)
case let .decryptedMessageService(randomId, action):
switch action {
case .decryptedMessageActionDeleteMessages:
@ -1618,9 +1618,9 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case .decryptedMessageActionReadMessages:
return nil
case .decryptedMessageActionScreenshotMessages:
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .historyScreenshot)]), [])
case let .decryptedMessageActionSetMessageTTL(ttlSeconds):
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), customStableId: nil, globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: authorId, text: "", attributes: [], media: [TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: ttlSeconds, autoSettingSource: nil))]), [])
case .decryptedMessageActionResend:
return nil
case .decryptedMessageActionRequestKey:

View file

@ -10,13 +10,6 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
]
}
var messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]] = [:]
for peerNamespace in peerIdNamespacesWithInitialCloudMessageHoles {
messageThreadHoles[peerNamespace] = [
Namespaces.Message.Cloud
]
}
// To avoid upgrading the database, **new** tags can be added here
// Uninitialized peers will fill the info using messageHoles
var upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [:]
@ -42,7 +35,15 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
),
messageHoles: messageHoles,
upgradedMessageHoles: upgradedMessageHoles,
messageThreadHoles: messageThreadHoles,
messageThreadHoles: { peerIdNamespace, threadId in
if threadId == Message.newTopicThreadId {
return nil
}
if peerIdNamespacesWithInitialCloudMessageHoles.contains(peerIdNamespace) {
return [Namespaces.Message.Cloud]
}
return nil
},
existingMessageTags: MessageTags.all,
messageTagsWithSummary: [.unseenPersonalMessage, .pinned, .video, .photo, .gif, .music, .voiceOrInstantVideo, .webPage, .file, .unseenReaction],
messageTagsWithThreadSummary: [.unseenPersonalMessage, .unseenReaction],
@ -76,10 +77,14 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
peerSummaryIsThreadBased: { peer, associatedPeer in
if let channel = peer as? TelegramChannel {
if channel.flags.contains(.isForum) {
if channel.flags.contains(.displayForumAsTabs) {
return (false, false)
} else {
if channel.linkedBotId != nil {
return (true, false)
} else {
if channel.flags.contains(.displayForumAsTabs) {
return (false, false)
} else {
return (true, false)
}
}
} else if channel.flags.contains(.isMonoforum) {
if let associatedPeer = associatedPeer as? TelegramChannel, associatedPeer.hasPermission(.manageDirect) {
@ -183,6 +188,14 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
}
return false
},
decodeAssociatedChatListPeerId: { cachedData in
if let cachedData = cachedData as? CachedUserData {
if case let .known(value) = cachedData.linkedBotChannelId {
return value
}
}
return nil
},
isPeerUpgradeMessage: { message in
for media in message.media {
if let action = media as? TelegramMediaAction {

View file

@ -219,6 +219,8 @@ public final class TelegramChannel: Peer, Equatable {
public var associatedPeerId: PeerId? {
if self.flags.contains(.isMonoforum) {
return self.linkedMonoforumId
} else if let linkedBotId = self.linkedBotId {
return linkedBotId
} else {
return nil
}
@ -259,7 +261,21 @@ public final class TelegramChannel: Peer, Equatable {
return mediaIds
}
public let notificationSettingsPeerId: PeerId? = nil
public var notificationSettingsPeerId: PeerId? {
if let linkedBotId = self.linkedBotId {
return linkedBotId
} else {
return nil
}
}
public var associatedPeerOverridesIdentity: Bool {
if self.linkedBotId != nil {
return true
} else {
return false
}
}
public var timeoutAttribute: UInt32? {
if let emojiStatus = self.emojiStatus {

View file

@ -3353,6 +3353,7 @@ func _internal_refreshInlineGroupCall(account: Account, messageId: MessageId) ->
}
return .update(StoreMessage(
id: currentMessage.id,
customStableId: nil,
globallyUniqueId: currentMessage.globallyUniqueId,
groupingKey: currentMessage.groupingKey,
threadId: currentMessage.threadId,

View file

@ -55,7 +55,7 @@ func _internal_applyMaxReadIndexInteractively(transaction: Transaction, stateMan
return currentAttribute
}
})
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: updatedAttributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: updatedAttributes, media: currentMessage.media))
})
}
break
@ -123,7 +123,7 @@ func applySecretOutgoingMessageReadActions(transaction: Transaction, id: Message
return currentAttribute
}
})
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: updatedAttributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: updatedAttributes, media: currentMessage.media))
})
}
break
@ -307,6 +307,12 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, netwo
}
}
}
if let user = peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum) {
if let cachedData = transaction.getPeerCachedData(peerId: user.id) as? CachedUserData, case let .known(linkedBotChannelId) = cachedData.linkedBotChannelId, let linkedBotChannelId {
_internal_togglePeerUnreadMarkInteractively(transaction: transaction, network: network, viewTracker: viewTracker, peerId: linkedBotChannelId, setToValue: false)
}
}
}
public func clearPeerUnseenPersonalMessagesInteractively(account: Account, peerId: PeerId, threadId: Int64?) -> Signal<Never, NoError> {

View file

@ -532,7 +532,7 @@ extension EngineChatList.Item {
}
var forumTopicDataValue: EngineChatList.ForumTopicData?
if let forumTopicData = forumTopicData {
if let forumTopicData {
let id = forumTopicData.id
if let forumTopicInfo = forumTopicData.info.data.get(MessageHistoryThreadData.self) {
forumTopicDataValue = EngineChatList.ForumTopicData(id: id, title: forumTopicInfo.info.title, iconFileId: forumTopicInfo.info.icon, iconColor: forumTopicInfo.info.iconColor, maxOutgoingReadMessageId: MessageId(peerId: index.messageIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: forumTopicInfo.maxOutgoingReadId), isUnread: forumTopicInfo.incomingUnreadCount > 0, threadPeer: forumTopicData.threadPeer.flatMap(EnginePeer.init))

View file

@ -174,7 +174,7 @@ func _internal_clearHistoryInteractively(postbox: Postbox, peerId: PeerId, threa
}
if addEmptyMessage {
let _ = transaction.addMessages([StoreMessage(id: topIndex.id, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: topIndex.timestamp, flags: StoreMessageFlags(), tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [TelegramMediaAction(action: .historyCleared)])], location: .Random)
let _ = transaction.addMessages([StoreMessage(id: topIndex.id, customStableId: nil, globallyUniqueId: nil, groupingKey: nil, threadId: nil, timestamp: topIndex.timestamp, flags: StoreMessageFlags(), tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: nil, text: "", attributes: [], media: [TelegramMediaAction(action: .historyCleared)])], location: .Random)
}
} else {
updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: peerId, minTimestamp: topIndex.timestamp, forceRootGroupIfNotExists: false)

View file

@ -111,7 +111,7 @@ func _internal_getMessagesFactCheckByPeerId(account: Account, peerId: EnginePeer
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
var attributes = currentMessage.attributes.filter { !($0 is FactCheckMessageAttribute) }
attributes.append(attribute)
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
index += 1

View file

@ -67,7 +67,7 @@ func _internal_installInteractiveReadMessagesAction(postbox: Postbox, stateManag
}
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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
if consumeMessageIds.contains(id) {
@ -151,7 +151,7 @@ private final class StoreOrUpdateMessageActionImpl: StoreOrUpdateMessageAction {
}
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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
transaction.setPendingMessageAction(type: .readReaction, id: id, action: ReadReactionAction())
}

View file

@ -120,7 +120,7 @@ func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messa
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)
}
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: updatedAttributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: updatedAttributes, media: currentMessage.media))
})
}
}
@ -153,7 +153,7 @@ func _internal_markReactionsAsSeenInteractively(postbox: Postbox, messageId: Mes
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}
@ -244,7 +244,7 @@ func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: M
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)
}
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: updatedAttributes, media: updatedMedia))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: updatedAttributes, media: updatedMedia))
})
}
}

View file

@ -1,6 +1,8 @@
import Postbox
public final class EngineMessage: Equatable {
public static let newTopicThreadId: Int64 = Message.newTopicThreadId
public typealias Id = MessageId
public typealias StableId = UInt32
public typealias Index = MessageIndex

View file

@ -233,7 +233,7 @@ private class ReplyThreadHistoryContextImpl {
)
}
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
@ -400,7 +400,7 @@ private class ReplyThreadHistoryContextImpl {
attributes[j] = updatedAttribute
}
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}

View file

@ -6,7 +6,6 @@ import MtProtoKit
func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: String?) -> Signal<Void, NoError> {
#if DEBUG
if "".isEmpty {
return account.postbox.loadedPeerWithId(botPeerId)
|> mapToSignal { botPeer -> Signal<Void, NoError> in
@ -34,11 +33,53 @@ func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: Str
}
}
return .complete()
if let payload = payload, !payload.isEmpty {
return account.postbox.loadedPeerWithId(botPeerId)
|> mapToSignal { botPeer -> Signal<Void, NoError> in
if let inputUser = apiInputUser(botPeer) {
return account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload))
|> mapToSignal { result -> Signal<Void, MTRpcError> in
account.stateManager.addUpdates(result)
return .complete()
}
|> `catch` { _ -> Signal<Void, MTRpcError> in
return .complete()
}
|> retryRequest
} else {
return .complete()
}
}
|> castError(MTRpcError.self)
} else {
return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]) |> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
|> castError(MTRpcError.self)
}
}
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}
}
if let payload = payload, !payload.isEmpty {
return account.postbox.loadedPeerWithId(botPeerId)
|> mapToSignal { botPeer -> Signal<Void, NoError> in
if let inputUser = apiInputUser(botPeer) {
return account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload))
|> mapToSignal { result -> Signal<Void, MTRpcError> in
account.stateManager.addUpdates(result)
return .complete()
}
|> `catch` { _ -> Signal<Void, MTRpcError> in
return .complete()
}
|> retryRequest
} else {
return .complete()
}
}
} else {
return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]) |> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
@ -46,7 +87,6 @@ func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: Str
}
}
}
#endif
if let payload = payload, !payload.isEmpty {
return account.postbox.loadedPeerWithId(botPeerId)

View file

@ -476,7 +476,7 @@ public extension TelegramEngine {
updatedMedia[i] = TelegramMediaMap(latitude: media.latitude, longitude: media.longitude, heading: media.heading, accuracyRadius: media.accuracyRadius, venue: media.venue, liveBroadcastingTimeout: max(0, timestamp - currentMessage.timestamp - 1), liveProximityNotificationRadius: nil)
}
}
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: currentMessage.attributes, media: updatedMedia))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: updatedMedia))
})
}
}
@ -616,7 +616,7 @@ public extension TelegramEngine {
attributes.append(AudioTranscriptionMessageAttribute(id: 0, text: text, isPending: !isFinal, didRate: false, error: error))
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
|> ignoreValues
@ -632,7 +632,7 @@ public extension TelegramEngine {
attributes.append(DerivedDataMessageAttribute(data: data))
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
|> ignoreValues
@ -654,7 +654,7 @@ public extension TelegramEngine {
attributes.append(DerivedDataMessageAttribute(data: data))
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
|> ignoreValues

View file

@ -26,7 +26,7 @@ func _internal_requestUpdateTodoMessageItems(account: Account, messageId: Messag
updatedCompletions.removeAll(where: { incompletedIds.contains($0.id) })
media = [todo.withUpdated(completions: updatedCompletions)]
}
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: currentMessage.attributes, media: media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: media))
})
return account.network.request(Api.functions.messages.toggleTodoCompleted(peer: inputPeer, msgId: messageId.id, completed: completedIds, incompleted: incompletedIds))
|> mapError { _ -> RequestUpdateTodoMessageError in
@ -59,7 +59,7 @@ func _internal_appendTodoMessageItems(account: Account, messageId: MessageId, it
updatedItems.append(contentsOf: items)
media = [todo.withUpdated(items: updatedItems)]
}
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: currentMessage.attributes, media: media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: media))
})
return account.network.request(Api.functions.messages.appendTodoList(peer: inputPeer, msgId: messageId.id, list: items.map { $0.apiItem }))
|> mapError { _ -> AppendTodoMessageError in

View file

@ -83,7 +83,7 @@ func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: Me
attributes.append(updatedAttribute)
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
if updatedAttribute.error == nil {
@ -111,6 +111,7 @@ func _internal_rateAudioTranscription(postbox: Postbox, network: Network, messag
}
return .update(StoreMessage(
id: currentMessage.id,
customStableId: nil,
globallyUniqueId: currentMessage.globallyUniqueId,
groupingKey: currentMessage.groupingKey,
threadId: currentMessage.threadId,

View file

@ -212,7 +212,7 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
attributes.append(updatedAttribute)
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
index += 1
@ -246,7 +246,7 @@ private func _internal_translateMessagesByPeerId(account: Account, peerId: Engin
let updatedAttribute: TranslationMessageAttribute = TranslationMessageAttribute(text: title.0, entities: title.1, additional: attrOptions, pollSolution: solution, toLang: toLang)
attributes.append(updatedAttribute)
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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))
})
}
}

View file

@ -95,7 +95,7 @@ func _internal_requestUpdatePinnedMessage(account: Account, peerId: PeerId, upda
if updatedTags == currentMessage.tags {
return .skip
}
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: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: currentMessage.media))
})
}
}
@ -204,7 +204,7 @@ func _internal_requestUnpinAllMessages(account: Account, peerId: PeerId, threadI
return .skip
}
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: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: currentMessage.media))
})
}
}

View file

@ -43,7 +43,7 @@ func _internal_storeMessageFromSearch(transaction: Transaction, message: Message
}
}
let storeMessage = StoreMessage(id: .Id(message.id), globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: StoreMessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media)
let storeMessage = StoreMessage(id: .Id(message.id), customStableId: nil, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, threadId: message.threadId, timestamp: message.timestamp, flags: StoreMessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: message.author?.id, text: message.text, attributes: message.attributes, media: message.media)
let _ = transaction.addMessages([storeMessage], location: .Random)
}

View file

@ -23,7 +23,7 @@ func updateMessageMedia(transaction: Transaction, id: MediaId, media: Media?) {
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)
}
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: currentMessage.attributes, media: currentMessage.media))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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: currentMessage.attributes, media: currentMessage.media))
})
}
}
@ -113,7 +113,7 @@ private func updateMessageThreadStatsInternal(transaction: Transaction, threadKe
channelThreadMessageId = attribute.messageId
}
}
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))
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, 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 let channelThreadMessageId = channelThreadMessageId {

View file

@ -128,6 +128,7 @@ public enum PresentationResourceKey: Int32 {
case chatListGeneralTopicIcon
case chatListGeneralTopicTemplateIcon
case chatListNewTopicTemplateIcon
case chatListGeneralTopicSmallIcon
case searchAdIcon

View file

@ -451,6 +451,23 @@ public struct PresentationResourcesChatList {
})
}
public static func newTopicTemplateIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatListNewTopicTemplateIcon.rawValue, { theme in
guard let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: .white) else {
return nil
}
let size = CGSize(width: 24.0, height: 24.0)
return generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
defer {
UIGraphicsPopContext()
}
image.draw(in: CGRect(origin: CGPoint(), size: size))
})?.withRenderingMode(.alwaysTemplate)
})
}
public static func statusAutoremoveIcon(_ theme: PresentationTheme, isActive: Bool) -> UIImage? {
return theme.image(PresentationResourceParameterKey.statusAutoremoveIcon(isActive: isActive), { theme in
return generateTintedImage(image: UIImage(bundleImageName: isActive ? "Chat List/StatusIconAutoremoveOn" : "Chat List/StatusIconAutoremoveOff"), color: isActive ? theme.list.itemAccentColor : theme.list.itemSecondaryTextColor)

View file

@ -26,13 +26,13 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
private var context: AccountContext?
private let containerNode: ContextControllerSourceNode
public let avatarNode: AvatarNode
public var avatarNode: AvatarNode
private var avatarVideoNode: AvatarVideoNode?
public private(set) var avatarStoryView: ComponentView<Empty>?
public var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
public let statusView: ComponentView<Empty>
public var statusView: ComponentView<Empty>
private var starView: StarView?
private var cachedDataDisposable = MetaDisposable()
@ -119,6 +119,13 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
public func setPeer(context: AccountContext, theme: PresentationTheme, peer: EnginePeer?, authorOfMessage: MessageReference? = nil, overrideImage: AvatarNodeImageOverride? = nil, emptyColor: UIColor? = nil, clipStyle: AvatarNodeClipStyle = .round, synchronousLoad: Bool = false, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), storeUnrounded: Bool = false) {
self.context = context
if let statusComponentView = self.statusView.view {
self.statusView = ComponentView()
statusComponentView.removeFromSuperview()
}
self.avatarNode.isHidden = false
self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: displayDimensions, storeUnrounded: storeUnrounded)
if let peer, peer.isSubscription {

View file

@ -479,7 +479,7 @@ private final class ChatMessagePeerContentNode: ASDisplayNode {
avatarIconContent = .topic(title: String(info.title.prefix(1)), color: info.iconColor, size: CGSize(width: 16.0, height: 16.0))
}
} else {
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(presentationData.theme.theme)?.withRenderingMode(.alwaysTemplate), tintColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper))
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicTemplateIcon(presentationData.theme.theme), tintColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper))
}
let avatarIconComponent = EmojiStatusComponent(

View file

@ -45,12 +45,18 @@ public final class ChatSideTopicsPanel: Component {
case top
}
public enum Kind {
case forum
case monoforum
case botForum
}
let context: AccountContext
let theme: PresentationTheme
let strings: PresentationStrings
let location: Location
let peerId: EnginePeer.Id
let isMonoforum: Bool
let kind: Kind
let topicId: Int64?
let controller: () -> ViewController?
let togglePanel: () -> Void
@ -63,7 +69,7 @@ public final class ChatSideTopicsPanel: Component {
strings: PresentationStrings,
location: Location,
peerId: EnginePeer.Id,
isMonoforum: Bool,
kind: Kind,
topicId: Int64?,
controller: @escaping () -> ViewController?,
togglePanel: @escaping () -> Void,
@ -75,7 +81,7 @@ public final class ChatSideTopicsPanel: Component {
self.strings = strings
self.location = location
self.peerId = peerId
self.isMonoforum = isMonoforum
self.kind = kind
self.topicId = topicId
self.controller = controller
self.togglePanel = togglePanel
@ -99,7 +105,7 @@ public final class ChatSideTopicsPanel: Component {
if lhs.peerId != rhs.peerId {
return false
}
if lhs.isMonoforum != rhs.isMonoforum {
if lhs.kind != rhs.kind {
return false
}
if lhs.topicId != rhs.topicId {
@ -386,6 +392,8 @@ public final class ChatSideTopicsPanel: Component {
} else {
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize)
}
} else if topicId == EngineMessage.newTopicThreadId {
avatarIconContent = .image(image: PresentationResourcesChatList.newTopicTemplateIcon(component.theme), tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.controlColor)
} else {
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicTemplateIcon(component.theme), tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.controlColor)
}
@ -429,6 +437,9 @@ public final class ChatSideTopicsPanel: Component {
let _ = topicId
if let threadData = component.item.item.threadData {
titleText = threadData.info.title
} else if topicId == EngineMessage.newTopicThreadId {
//TODO:localize
titleText = "New Chat"
} else {
titleText = " "
}
@ -824,6 +835,8 @@ public final class ChatSideTopicsPanel: Component {
} else {
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize)
}
} else if topicId == EngineMessage.newTopicThreadId {
avatarIconContent = .image(image: PresentationResourcesChatList.newTopicTemplateIcon(component.theme), tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.controlColor)
} else {
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicTemplateIcon(component.theme), tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.controlColor)
}
@ -861,6 +874,9 @@ public final class ChatSideTopicsPanel: Component {
let _ = topicId
if let threadData = component.item.item.threadData {
titleText = threadData.info.title
} else if topicId == EngineMessage.newTopicThreadId {
//TODO:localize
titleText = "New Chat"
} else {
titleText = " "
}
@ -1144,12 +1160,14 @@ public final class ChatSideTopicsPanel: Component {
private final class VerticalAllItemComponent: Component, AllItemComponent {
let isSelected: Bool
let kind: ChatSideTopicsPanel.Kind
let theme: PresentationTheme
let strings: PresentationStrings
let action: (() -> Void)?
init(isSelected: Bool, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) {
init(isSelected: Bool, kind: ChatSideTopicsPanel.Kind, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) {
self.isSelected = isSelected
self.kind = kind
self.theme = theme
self.strings = strings
self.action = action
@ -1162,6 +1180,9 @@ public final class ChatSideTopicsPanel: Component {
if lhs.isSelected != rhs.isSelected {
return false
}
if lhs.kind != rhs.kind {
return false
}
if lhs.theme !== rhs.theme {
return false
}
@ -1234,7 +1255,13 @@ public final class ChatSideTopicsPanel: Component {
containerSize: CGSize(width: 100.0, height: 100.0)
)
let titleText: String = component.strings.Chat_InlineTopicMenu_AllTab
let titleText: String
if case .botForum = component.kind {
//TODO:localize
titleText = "General"
} else {
titleText = component.strings.Chat_InlineTopicMenu_AllTab
}
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
@ -1284,12 +1311,14 @@ public final class ChatSideTopicsPanel: Component {
private final class HorizontalAllItemComponent: Component, AllItemComponent {
let isSelected: Bool
let kind: ChatSideTopicsPanel.Kind
let theme: PresentationTheme
let strings: PresentationStrings
let action: (() -> Void)?
init(isSelected: Bool, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) {
init(isSelected: Bool, kind: ChatSideTopicsPanel.Kind, theme: PresentationTheme, strings: PresentationStrings, action: (() -> Void)?) {
self.isSelected = isSelected
self.kind = kind
self.theme = theme
self.strings = strings
self.action = action
@ -1302,6 +1331,9 @@ public final class ChatSideTopicsPanel: Component {
if lhs.isSelected != rhs.isSelected {
return false
}
if lhs.kind != rhs.kind {
return false
}
if lhs.theme !== rhs.theme {
return false
}
@ -1361,7 +1393,13 @@ public final class ChatSideTopicsPanel: Component {
let leftInset: CGFloat = 6.0
let rightInset: CGFloat = 12.0
let titleText: String = component.strings.Chat_InlineTopicMenu_AllTab
let titleText: String
if case .botForum = component.kind {
//TODO:localize
titleText = "General"
} else {
titleText = component.strings.Chat_InlineTopicMenu_AllTab
}
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
@ -1372,7 +1410,7 @@ public final class ChatSideTopicsPanel: Component {
containerSize: CGSize(width: 400.0, height: 200.0)
)
let contentSize: CGFloat = leftInset + rightInset + titleSize.height
let contentSize: CGFloat = leftInset + rightInset + titleSize.width
let size = CGSize(width: contentSize, height: availableSize.height)
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - titleSize.height) * 0.5)), size: titleSize)
@ -1422,6 +1460,7 @@ public final class ChatSideTopicsPanel: Component {
private var tabItemView: TabItemView?
private var peerId: EnginePeer.Id?
private var rawItems: [Item] = []
private var reorderingItems: [Item]?
private var resetReorderingOnNextUpdate: Bool = false
@ -1653,21 +1692,99 @@ public final class ChatSideTopicsPanel: Component {
}
if self.component == nil {
let threadListSignal: Signal<EngineChatList, NoError> = component.context.sharedContext.subscribeChatListData(context: component.context, location: component.isMonoforum ? .savedMessagesChats(peerId: component.peerId) : .forum(peerId: component.peerId))
let threadListSignal: Signal<(EnginePeer.Id, EngineChatList), NoError>
switch component.kind {
case .botForum:
let forumPeerId: Signal<EnginePeer.Id?, NoError>
if component.peerId.namespace == Namespaces.Peer.CloudUser {
forumPeerId = component.context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: component.peerId)
)
|> map { value -> EnginePeer.Id? in
if case let .known(value) = value {
return value
} else {
return nil
}
}
|> distinctUntilChanged
} else {
forumPeerId = .single(component.peerId)
}
let defaultPeerId = component.peerId
threadListSignal = forumPeerId
|> mapToSignal { forumPeerId -> Signal<(EnginePeer.Id, EngineChatList), NoError> in
if let forumPeerId {
return component.context.sharedContext.subscribeChatListData(context: component.context, location: .forum(peerId: forumPeerId))
|> map { value in
return (forumPeerId, value)
}
} else {
return .single((defaultPeerId, EngineChatList(items: [], groupItems: [], additionalItems: [], hasEarlier: false, hasLater: false, isLoading: false)))
}
}
default:
let defaultPeerId = component.peerId
threadListSignal = component.context.sharedContext.subscribeChatListData(context: component.context, location: component.kind == .monoforum ? .savedMessagesChats(peerId: component.peerId) : .forum(peerId: component.peerId))
|> map { value in
return (defaultPeerId, value)
}
}
self.itemsDisposable = (threadListSignal
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
guard let self else {
|> deliverOnMainQueue).startStrict(next: { [weak self] peerId, chatList in
guard let self, let component = self.component else {
return
}
self.peerId = peerId
let wasEmpty = self.rawItems.isEmpty
self.rawItems.removeAll()
for item in chatList.items.reversed() {
if case .botForum = component.kind, case let .forum(topicId) = item.id, topicId == 1 {
#if DEBUG && false
#else
continue
#endif
}
self.rawItems.append(Item(item: item))
}
if case .botForum = component.kind, !self.rawItems.contains(where: { item in
if case let .forum(topicId) = item.id {
return topicId == EngineMessage.newTopicThreadId
} else {
return false
}
}) {
self.rawItems.insert(Item(item: EngineChatList.Item(
id: .forum(EngineMessage.newTopicThreadId),
index: EngineChatList.Item.Index.forum(pinnedIndex: .none, timestamp: Int32.max - 1, threadId: EngineMessage.newTopicThreadId, namespace: Namespaces.Message.Local, id: 1),
messages: [],
readCounters: nil,
isMuted: false,
draft: nil,
threadData: nil,
renderedPeer: EngineRenderedPeer(peerId: peerId, peers: [:], associatedMedia: [:]),
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
forumTopicData: nil,
topForumTopicItems: [],
hasFailed: false,
isContact: false,
autoremoveTimeout: nil,
storyStats: nil,
displayAsTopicList: false,
isPremiumRequiredToMessage: false,
mediaDraftContentType: nil
)), at: 0)
}
if self.reorderingItems != nil {
self.reorderingItems = self.rawItems
}
@ -1786,7 +1903,7 @@ public final class ChatSideTopicsPanel: Component {
itemTransition = .immediate
animateIn = true
itemView = TabItemView(context: component.context, action: { [weak self] in
guard let self, let component = self.component else {
guard let self, let peerId = self.peerId, let component = self.component else {
return
}
if self.isReordering {
@ -1806,7 +1923,7 @@ public final class ChatSideTopicsPanel: Component {
}
if threadIds != currentThreadIds {
let _ = component.context.engine.peers.setForumChannelPinnedTopics(id: component.peerId, threadIds: threadIds).startStandalone()
let _ = component.context.engine.peers.setForumChannelPinnedTopics(id: peerId, threadIds: threadIds).startStandalone()
self.resetReorderingOnNextUpdate = true
} else {
self.reorderingItems = nil
@ -1880,6 +1997,7 @@ public final class ChatSideTopicsPanel: Component {
id: ScrollId.all,
component: AnyComponent(VerticalAllItemComponent(
isSelected: component.topicId == nil,
kind: component.kind,
theme: component.theme,
strings: component.strings,
action: { [weak self] in
@ -1895,6 +2013,7 @@ public final class ChatSideTopicsPanel: Component {
id: ScrollId.all,
component: AnyComponent(HorizontalAllItemComponent(
isSelected: component.topicId == nil,
kind: component.kind,
theme: component.theme,
strings: component.strings,
action: { [weak self] in
@ -1937,10 +2056,10 @@ public final class ChatSideTopicsPanel: Component {
component.updateTopicId(topicId, direction)
}
var itemContextGesture: ((ContextGesture, ContextExtractedContentContainingNode) -> Void)?
if !self.isReordering && component.isMonoforum {
if !self.isReordering, case .monoforum = component.kind {
itemContextGesture = { [weak self] gesture, sourceNode in
Task { @MainActor in
guard let self, let component = self.component else {
guard let self, let peerId = self.peerId, let component = self.component else {
return
}
guard let controller = component.controller() else {
@ -1964,18 +2083,18 @@ public final class ChatSideTopicsPanel: Component {
var items: [ContextMenuItem] = []
let threadInfo = await component.context.engine.data.get(
TelegramEngine.EngineData.Item.Messages.ThreadInfo(peerId: component.peerId, threadId: topicId)
TelegramEngine.EngineData.Item.Messages.ThreadInfo(peerId: peerId, threadId: topicId)
).get()
if let threadInfo, threadInfo.isMessageFeeRemoved {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ReinstatePaidMessages, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
guard let self, let component = self.component else {
guard let self, let peerId = self.peerId, let component = self.component else {
return
}
c?.dismiss(completion: {})
let _ = component.context.engine.peers.reinstateNoPaidMessagesException(scopePeerId: component.peerId, peerId: EnginePeer.Id(topicId)).startStandalone()
let _ = component.context.engine.peers.reinstateNoPaidMessagesException(scopePeerId: peerId, peerId: EnginePeer.Id(topicId)).startStandalone()
})))
}
@ -2011,7 +2130,7 @@ public final class ChatSideTopicsPanel: Component {
}
} else if !self.isReordering {
itemContextGesture = { [weak self] gesture, sourceNode in
guard let self, let component = self.component else {
guard let self, let peerId = self.peerId, let component = self.component else {
return
}
guard let controller = component.controller() else {
@ -2043,7 +2162,7 @@ public final class ChatSideTopicsPanel: Component {
let _ = (chatForumTopicMenuItems(
context: component.context,
peerId: component.peerId,
peerId: peerId,
threadId: topicId,
isPinned: isPinned,
isClosed: isClosed,
@ -2052,12 +2171,12 @@ public final class ChatSideTopicsPanel: Component {
canSelect: false,
customEdit: { [weak self] contextController in
contextController.dismiss(completion: {
guard let self, let component = self.component, let threadData else {
guard let self, let peerId = self.peerId, let component = self.component, let threadData else {
return
}
let editController = component.context.sharedContext.makeEditForumTopicScreen(
context: component.context,
peerId: component.peerId,
peerId: peerId,
threadId: topicId,
threadInfo: threadData.info,
isHidden: threadData.isHidden
@ -2066,7 +2185,7 @@ public final class ChatSideTopicsPanel: Component {
})
},
customPinUnpin: { [weak self] contextController in
guard let self, let component = self.component else {
guard let self, let peerId = self.peerId, let component = self.component else {
contextController.dismiss(completion: {})
return
}
@ -2074,7 +2193,7 @@ public final class ChatSideTopicsPanel: Component {
self.isTogglingPinnedItem = true
self.dismissContextControllerOnNextUpdate = contextController
let _ = (component.context.engine.peers.toggleForumChannelTopicPinned(id: component.peerId, threadId: topicId)
let _ = (component.context.engine.peers.toggleForumChannelTopicPinned(id: peerId, threadId: topicId)
|> deliverOnMainQueue).startStandalone(error: { [weak self, weak contextController] error in
guard let self, let component = self.component else {
contextController?.dismiss(completion: {})
@ -2099,6 +2218,12 @@ public final class ChatSideTopicsPanel: Component {
return
}
self.updateIsReordering(isReordering: true)
},
onDeleted: { [weak self] in
guard let self, let component = self.component else {
return
}
component.updateTopicId(nil, false)
}
)
|> take(1)

View file

@ -1263,15 +1263,18 @@ final class PeerInfoHeaderNode: ASDisplayNode {
subtitleColor = UIColor.white
let statusText: String
statusText = peer.debugDisplayTitle
if let channel = peer as? TelegramChannel, channel.linkedBotId != nil {
statusText = " "
} else {
statusText = peer.debugDisplayTitle
subtitleIsButton = true
}
subtitleStringText = statusText
subtitleAttributes = MultiScaleTextState.Attributes(font: Font.semibold(16.0), color: subtitleColor)
smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor)
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white))
subtitleIsButton = true
let (maybePanelStatusData, _, _) = panelStatusData
if let panelStatusData = maybePanelStatusData {
@ -1979,7 +1982,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.currentPendingStarRating = nil
}
#if DEBUG
#if DEBUG && false
if "".isEmpty {
let starRating: TelegramStarRating

View file

@ -12680,7 +12680,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
}
if let data = self.data, !data.isPremiumRequiredForStoryPosting || data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories) {
if let data = self.data, !data.isPremiumRequiredForStoryPosting || data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories), channel.linkedBotId == nil {
rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0)
} else if self.isMyProfile {
rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0)

View file

@ -130,6 +130,9 @@ extension ChatControllerImpl {
self.contentDataDisposable?.dispose()
self.newTopicEventsDisposable?.dispose()
self.newTopicEventsDisposable = nil
let configuration: Signal<ChatControllerImpl.ContentData.Configuration, NoError> = self.presentationInterfaceStatePromise.get()
|> map { presentationInterfaceState -> ChatControllerImpl.ContentData.Configuration in
return ChatControllerImpl.ContentData.Configuration(
@ -166,6 +169,10 @@ extension ChatControllerImpl {
if let historyNodeData = contentData.state.historyNodeData {
self.updateChatLocationToOther(chatLocation: historyNodeData.chatLocation)
return
} else if case let .botForumThread(linkedForumId, threadId) = self.subject {
self.subject = nil
self.updateInitialChatBotForumLocationThread(linkedForumId: linkedForumId, threadId: threadId)
return
}
apply({ [weak self, weak contentData] forceAnimation in
@ -202,6 +209,25 @@ extension ChatControllerImpl {
self.contentDataUpdated(synchronous: false, forceAnimation: false, previousState: previousState)
}
})
if self.newTopicEventsDisposable == nil, let peerId = chatLocation.peerId, chatLocation.threadId == EngineMessage.newTopicThreadId {
self.newTopicEventsDisposable = (self.context.account.pendingMessageManager.newTopicEvents(peerId: peerId)
|> mapToSignal { event -> Signal<Int64, NoError> in
if case let .didMove(fromThreadId, toThreadId) = event {
if fromThreadId == EngineMessage.newTopicThreadId {
return .single(toThreadId)
}
}
return .never()
}
|> take(1)
|> deliverOnMainQueue).startStrict(next: { [weak self] threadId in
guard let self else {
return
}
self.updateInitialChatBotForumLocationThread(linkedForumId: peerId, threadId: threadId)
})
}
})
}

View file

@ -123,6 +123,7 @@ import ChatEmptyNode
import ChatMediaInputStickerGridItem
import AdsInfoScreen
import FaceScanScreen
import ForumCreateTopicScreen
extension ChatControllerImpl {
func openPeer(peer: EnginePeer?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: MessageReference?, fromReactionMessageId: MessageId? = nil, expandAvatar: Bool = false, peerTypes: ReplyMarkupButtonAction.PeerTypes? = nil, skipAgeVerification: Bool = false) {
@ -328,4 +329,106 @@ extension ChatControllerImpl {
}
})
}
func openBotForumMoreMenu(sourceView: UIView, gesture: ContextGesture?) {
Task { @MainActor [weak self] in
guard let self, let peerId = self.chatLocation.peerId else {
return
}
guard let forumPeerId = await (self.context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: peerId)
)
|> map { value -> EnginePeer.Id? in
if case let .known(value) = value {
return value
} else {
return nil
}
}).get() else {
return
}
let strings = self.presentationData.strings
var items: [ContextMenuItem] = []
//TODO:localize
items.append(.action(ContextMenuActionItem(text: "Open Profile", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
f(.default)
guard let self, let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else {
return
}
guard let controller = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else {
return
}
(self.navigationController as? NavigationController)?.pushViewController(controller)
})))
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] action in
action.dismissWithResult(.default)
self?.beginMessageSearch("")
})))
items.append(.action(ContextMenuActionItem(text: strings.Chat_CreateTopic, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] action in
guard let self else {
return
}
action.dismissWithResult(.default)
let controller = ForumCreateTopicScreen(context: self.context, peerId: forumPeerId, mode: .create)
controller.navigationPresentation = .modal
controller.completion = { [weak self, weak controller] title, fileId, iconColor, _ in
controller?.isInProgress = true
controller?.view.endEditing(true)
guard let self else {
return
}
let _ = (self.context.engine.peers.createForumChannelTopic(id: forumPeerId, title: title, iconColor: iconColor, iconFileId: fileId)
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak controller] topicId in
guard let self else {
return
}
self.updateChatLocationThread(threadId: topicId)
controller?.dismiss()
}, error: { _ in
controller?.isInProgress = false
})
}
self.push(controller)
})))
let presentationData = self.presentationData
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
self.presentInGlobalOverlay(contextController)
}
}
}
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceView: UIView
init(controller: ViewController, sourceView: UIView) {
self.controller = controller
self.sourceView = sourceView
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View file

@ -247,7 +247,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let context: AccountContext
public internal(set) var chatLocation: ChatLocation
public let subject: ChatControllerSubject?
public internal(set) var subject: ChatControllerSubject?
var botStart: ChatControllerInitialBotStart?
var attachBotStart: ChatControllerInitialAttachBotStart?
@ -258,6 +258,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var contentData: ChatControllerImpl.ContentData?
let contentDataReady = ValuePromise<Bool>(false, ignoreRepeated: true)
var contentDataDisposable: Disposable?
var newTopicEventsDisposable: Disposable?
var didHandlePerformDismissAction: Bool = false
var didInitializePersistentPeerInterfaceData: Bool = false
@ -703,6 +704,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.stickerSettings = ChatInterfaceStickerSettings()
var subject = subject
if case .botForumThread = subject {
subject = nil
}
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, subject: subject, peerNearbyData: peerNearbyData, greetingData: context.prefetchManager?.preloadedGreetingSticker, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
if case let .customChatContents(customChatContents) = subject {
@ -5528,6 +5534,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if peerId == self.context.account.peerId {
PeerInfoScreenImpl.openSavedMessagesMoreMenu(context: self.context, sourceController: self, isViewingAsTopics: false, sourceView: sourceNode.view, gesture: gesture)
} else if peerId.namespace == Namespaces.Peer.CloudUser {
self.openBotForumMoreMenu(sourceView: sourceNode.view, gesture: gesture)
} else {
ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, isViewingAsTopics: false, sourceView: sourceNode.view, gesture: gesture)
}
@ -6179,6 +6187,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.updateChatLocationThreadDisposable?.dispose()
self.accountPeerDisposable?.dispose()
self.contentDataDisposable?.dispose()
self.newTopicEventsDisposable?.dispose()
self.updateMessageTodoDisposables?.dispose()
self.preloadNextChatPeerIdDisposable.dispose()
}
@ -8827,7 +8836,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
#if DEBUG
#if DEBUG && false
let botForumId = await self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: peerId)
).get()
@ -10144,17 +10153,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) {
func updateInitialChatBotForumLocationThread(linkedForumId: EnginePeer.Id, threadId: Int64) {
if self.isUpdatingChatLocationThread {
return
}
guard let peerId = self.chatLocation.peerId else {
return
}
if self.chatLocation.threadId == threadId {
return
}
guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else {
assertionFailure()
return
}
@ -10162,34 +10163,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.chatDisplayNode.dismissTextInput()
let updatedChatLocation: ChatLocation
if let threadId {
var isMonoforum = false
if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
isMonoforum = true
}
updatedChatLocation = .replyThread(message: ChatReplyThreadMessage(
peerId: peerId,
threadId: threadId,
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforumPost: isMonoforum,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
unreadCount: 0,
initialFilledHoles: IndexSet(),
initialAnchor: .automatic,
isNotAvailable: false
))
} else {
updatedChatLocation = .peer(id: peerId)
}
let navigationSnapshot = self.chatTitleView?.prepareSnapshotState()
let avatarSnapshot = self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil ? (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState() : nil
let updatedChatLocation: ChatLocation = .replyThread(message: ChatReplyThreadMessage(
peerId: linkedForumId,
threadId: threadId,
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforumPost: false,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
unreadCount: 0,
initialFilledHoles: IndexSet(),
initialAnchor: .automatic,
isNotAvailable: false
))
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder)
@ -10199,38 +10187,139 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
self.currentChatSwitchDirection = animationDirection
self.currentChatSwitchDirection = nil
self.chatLocation = updatedChatLocation
historyNode.areContentAnimationsEnabled = true
self.chatDisplayNode.prepareSwitchToChatLocation(historyNode: historyNode, animationDirection: animationDirection)
self.chatDisplayNode.prepareSwitchToChatLocation(historyNode: historyNode, animationDirection: nil)
apply(true)
if let navigationSnapshot, let animationDirection {
let mappedAnimationDirection: ChatTitleView.AnimateFromSnapshotDirection
switch animationDirection {
case .up:
mappedAnimationDirection = .up
case .down:
mappedAnimationDirection = .down
case .left:
mappedAnimationDirection = .left
case .right:
mappedAnimationDirection = .right
}
self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection)
}
if let avatarSnapshot, self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil {
(self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.animateFromSnapshot(avatarSnapshot)
}
self.currentChatSwitchDirection = nil
self.isUpdatingChatLocationThread = false
})
}
public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) {
Task { @MainActor [weak self] in
guard let self else {
return
}
if self.isUpdatingChatLocationThread {
return
}
guard let peerId = self.chatLocation.peerId else {
return
}
if self.chatLocation.threadId == threadId {
return
}
guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else {
return
}
self.saveInterfaceState()
self.chatDisplayNode.dismissTextInput()
let updatedChatLocation: ChatLocation
if let threadId {
if let user = peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum) {
guard case let .known(linkedForumId) = await self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.LinkedBotForumPeerId(id: user.id)
).get(), let linkedForumId else {
return
}
updatedChatLocation = .replyThread(message: ChatReplyThreadMessage(
peerId: linkedForumId,
threadId: threadId,
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforumPost: false,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
unreadCount: 0,
initialFilledHoles: IndexSet(),
initialAnchor: .automatic,
isNotAvailable: false
))
} else {
var isMonoforum = false
if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
isMonoforum = true
}
updatedChatLocation = .replyThread(message: ChatReplyThreadMessage(
peerId: peerId,
threadId: threadId,
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforumPost: isMonoforum,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
unreadCount: 0,
initialFilledHoles: IndexSet(),
initialAnchor: .automatic,
isNotAvailable: false
))
}
} else {
if let channel = peer as? TelegramChannel, let linkedBotId = channel.linkedBotId {
updatedChatLocation = .peer(id: linkedBotId)
} else {
updatedChatLocation = .peer(id: peerId)
}
}
let navigationSnapshot = self.chatTitleView?.prepareSnapshotState()
let avatarSnapshot = self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil ? (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState() : nil
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder)
self.isUpdatingChatLocationThread = true
self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, apply: { [weak self, weak historyNode] apply in
guard let self, let historyNode else {
return
}
self.currentChatSwitchDirection = animationDirection
self.chatLocation = updatedChatLocation
historyNode.areContentAnimationsEnabled = true
self.chatDisplayNode.prepareSwitchToChatLocation(historyNode: historyNode, animationDirection: animationDirection)
apply(true)
if let navigationSnapshot, let animationDirection {
let mappedAnimationDirection: ChatTitleView.AnimateFromSnapshotDirection
switch animationDirection {
case .up:
mappedAnimationDirection = .up
case .down:
mappedAnimationDirection = .down
case .left:
mappedAnimationDirection = .left
case .right:
mappedAnimationDirection = .right
}
self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection)
}
if let avatarSnapshot, self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil {
(self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.animateFromSnapshot(avatarSnapshot)
}
self.currentChatSwitchDirection = nil
self.isUpdatingChatLocationThread = false
})
}
}
public var contentContainerNode: ASDisplayNode {
return self.chatDisplayNode.contentContainerNode
}

View file

@ -811,7 +811,6 @@ extension ChatControllerImpl {
var sendPaidMessageStars: StarsAmount?
var alwaysShowGiftButton = false
var disallowedGifts: TelegramDisallowedGifts?
var switchToBotForum: EnginePeer.Id?
if let peer = peerView.peers[peerView.peerId] {
if let cachedData = peerView.cachedData as? CachedUserData {
contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil, managingBot: managingBot)
@ -826,11 +825,6 @@ extension ChatControllerImpl {
}
disallowedGifts = cachedData.disallowedGifts
}
if case let .known(value) = cachedData.linkedBotChannelId {
if let value {
switchToBotForum = value
}
}
} else if let cachedData = peerView.cachedData as? CachedGroupData {
var invitedBy: Peer?
if let invitedByPeerId = cachedData.invitedBy {
@ -1030,15 +1024,6 @@ extension ChatControllerImpl {
strongSelf.state.renderedPeer = renderedPeer
strongSelf.state.adMessage = adMessage
if let switchToBotForum {
strongSelf.state.historyNodeData = HistoryNodeData(
chatLocation: .peer(id: switchToBotForum),
chatLocationContextHolder: Atomic(value: nil)
)
} else {
strongSelf.state.historyNodeData = nil
}
if case .standard(.default) = mode, let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info {
var isRegularChat = false
@ -1459,12 +1444,19 @@ extension ChatControllerImpl {
)
var customMessageCount: Int?
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.isMonoForum {
var customSubtitle: String?
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel {
if peer.isMonoForum {
} else if peer.linkedBotId != nil {
customSubtitle = strongSelf.presentationData.strings.Bot_GenericBotStatus
} else {
customMessageCount = savedMessagesPeer?.messageCount ?? 0
}
} else {
customMessageCount = savedMessagesPeer?.messageCount ?? 0
}
strongSelf.state.chatTitleContent = .peer(peerView: mappedPeerData, customTitle: nil, customSubtitle: nil, onlineMemberCount: (nil, nil), isScheduledMessages: false, isMuted: false, customMessageCount: customMessageCount, isEnabled: true)
strongSelf.state.chatTitleContent = .peer(peerView: mappedPeerData, customTitle: nil, customSubtitle: customSubtitle, onlineMemberCount: (nil, nil), isScheduledMessages: false, isMuted: false, customMessageCount: customMessageCount, isEnabled: true)
strongSelf.state.peerView = peerView
@ -1551,7 +1543,15 @@ extension ChatControllerImpl {
}
if let threadInfo = messageAndTopic.threadData?.info {
strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: threadInfo.title, customSubtitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true)
var customSubtitle: String?
if messageAndTopic.messageCount == 0, let peer = peerView.peers[peerView.peerId] as? TelegramChannel {
if peer.linkedBotId != nil {
//TODO:localize
customSubtitle = "topic"
}
}
strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: threadInfo.title, customSubtitle: customSubtitle, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true)
let avatarContent: EmojiStatusComponent.Content
if chatLocation.threadId == 1 {
@ -1569,6 +1569,10 @@ extension ChatControllerImpl {
infoContextActionIsEnabled = true
}
strongSelf.state.infoAvatar = .emojiStatus(content: avatarContent, contextActionIsEnabled: infoContextActionIsEnabled)
} else if chatLocation.threadId == EngineMessage.newTopicThreadId {
//TODO:localize
strongSelf.state.chatTitleContent = .custom("New Chat", nil, false)
strongSelf.state.infoAvatar = nil
} else {
strongSelf.state.chatTitleContent = .replyThread(type: replyThreadType, count: count)
}

View file

@ -1381,7 +1381,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
let currentViewVersion = self.currentViewVersion
let historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange<Int32>?, Set<MessageId>), NoError>
var historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange<Int32>?, Set<MessageId>), NoError>
var isFirstTime = true
var updateAllOnEachVersion = false
if case let .custom(messages, at, quote, _) = self.source {
@ -1746,6 +1746,33 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
}
|> distinctUntilChanged
let stopHistoryViewUpdates: Signal<Bool, NoError>
if let peerId = chatLocation.peerId, chatLocation.threadId == EngineMessage.newTopicThreadId {
stopHistoryViewUpdates = Signal<Bool, NoError>.single(false)
|> then(
self.context.account.pendingMessageManager.newTopicEvents(peerId: peerId)
|> mapToSignal { event -> Signal<Bool, NoError> in
if case .willMove(EngineMessage.newTopicThreadId, _) = event {
return .single(true)
} else {
return .never()
}
}
|> take(1)
)
} else {
stopHistoryViewUpdates = .single(false)
}
let historyViewUpdateValue = historyViewUpdate
historyViewUpdate = stopHistoryViewUpdates |> mapToSignal { value in
if value {
return .never()
} else {
return historyViewUpdateValue
}
}
let startTime = CFAbsoluteTimeGetCurrent()
var measure_isFirstTime = true
let messageViewQueue = Queue.mainQueue()

View file

@ -132,6 +132,13 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present
}
}
if let user = presentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), let moreInfoNavigationButton = moreInfoNavigationButton {
if case .pinnedMessages = presentationInterfaceState.subject {
} else {
return moreInfoNavigationButton
}
}
if case .messageOptions = presentationInterfaceState.subject {
return nil
}

View file

@ -237,7 +237,7 @@ func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfa
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
return currentPanel
} else {
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: true)
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, kind: .monoforum)
panel.interfaceInteraction = interfaceInteraction
return panel
}
@ -248,7 +248,18 @@ func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfa
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
return currentPanel
} else {
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: false)
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, kind: channel.linkedBotId != nil ? .botForum : .forum)
panel.interfaceInteraction = interfaceInteraction
return panel
}
}
} else if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), chatPresentationInterfaceState.search == nil {
let topicListDisplayModeOnTheSide = chatPresentationInterfaceState.persistentData.topicListPanelLocation
if !topicListDisplayModeOnTheSide, let peerId = chatPresentationInterfaceState.chatLocation.peerId {
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
return currentPanel
} else {
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, kind: .botForum)
panel.interfaceInteraction = interfaceInteraction
return panel
}
@ -279,7 +290,7 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState
strings: chatPresentationInterfaceState.strings,
location: .side,
peerId: peerId,
isMonoforum: true,
kind: .monoforum,
topicId: chatPresentationInterfaceState.chatLocation.threadId,
controller: { [weak interfaceInteraction] in
return interfaceInteraction?.chatController()
@ -310,7 +321,38 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState
strings: chatPresentationInterfaceState.strings,
location: .side,
peerId: peerId,
isMonoforum: false,
kind: channel.linkedBotId != nil ? .botForum : .forum,
topicId: chatPresentationInterfaceState.chatLocation.threadId,
controller: { [weak interfaceInteraction] in
return interfaceInteraction?.chatController()
},
togglePanel: { [weak interfaceInteraction] in
interfaceInteraction?.toggleChatSidebarMode()
},
updateTopicId: { [weak interfaceInteraction] topicId, direction in
interfaceInteraction?.updateChatLocationThread(topicId, direction ? .down : .up)
},
openDeletePeer: { [weak interfaceInteraction] threadId in
guard let controller = interfaceInteraction?.chatController() as? ChatControllerImpl else {
return
}
controller.openDeleteMonoforumPeer(peerId: EnginePeer.Id(threadId))
}
))
)
}
} else if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), chatPresentationInterfaceState.search == nil {
let topicListDisplayModeOnTheSide = chatPresentationInterfaceState.persistentData.topicListPanelLocation
if topicListDisplayModeOnTheSide {
return AnyComponentWithIdentity(
id: "topics",
component: AnyComponent(ChatSideTopicsPanel(
context: context,
theme: chatPresentationInterfaceState.theme,
strings: chatPresentationInterfaceState.strings,
location: .side,
peerId: peerId,
kind: .botForum,
topicId: chatPresentationInterfaceState.chatLocation.threadId,
controller: { [weak interfaceInteraction] in
return interfaceInteraction?.chatController()

View file

@ -53,17 +53,15 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
private let context: AccountContext
private let peerId: EnginePeer.Id
private let isMonoforum: Bool
private let kind: ChatSideTopicsPanel.Kind
private let panel = ComponentView<ChatSidePanelEnvironment>()
init(context: AccountContext, peerId: EnginePeer.Id, isMonoforum: Bool) {
init(context: AccountContext, peerId: EnginePeer.Id, kind: ChatSideTopicsPanel.Kind) {
self.context = context
self.peerId = peerId
self.isMonoforum = isMonoforum
self.kind = kind
super.init()
}
deinit {
@ -103,7 +101,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
strings: params.interfaceState.strings,
location: .top,
peerId: self.peerId,
isMonoforum: self.isMonoforum,
kind: self.kind,
topicId: params.interfaceState.chatLocation.threadId,
controller: { [weak self] in
return self?.interfaceInteraction?.chatController()