[WIP] Monoforums

This commit is contained in:
Isaac 2025-05-06 17:00:11 +02:00
parent 22ea05b3aa
commit 28f246749f
30 changed files with 416 additions and 253 deletions

View File

@ -6,7 +6,7 @@ import TelegramCore
public enum ChatListControllerLocation: Equatable {
case chatList(groupId: EngineChatList.Group)
case forum(peerId: EnginePeer.Id)
case savedMessagesChats
case savedMessagesChats(peerId: EnginePeer.Id)
}
public protocol ChatListController: ViewController {

View File

@ -1030,9 +1030,20 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
guard let self else {
return
}
let _ = (self.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)])
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] combinedView in
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
}
@ -1040,19 +1051,30 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
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 let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
openAsInlineForum = false
} else {
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
openAsInlineForum = false
}
}
if openAsInlineForum, case let .channel(channel) = peer, channel.flags.contains(.isForum), threadId == nil {
self.chatListDisplayNode.clearHighlightAnimated(true)
if self.chatListDisplayNode.inlineStackContainerNode?.location == .forum(peerId: channel.id) {
self.setInlineChatList(location: nil)
} else {
@ -1062,7 +1084,28 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId {
let _ = self.context.sharedContext.navigateToForumThread(context: self.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: scrollToEndIfExists, keepStack: .never).startStandalone()
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
navigationController: navigationController,
context: self.context,
chatLocation: .replyThread(ChatReplyThreadMessage(
peerId: peer.id,
threadId: threadId,
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforum: channel.flags.contains(.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 = []
@ -1476,7 +1519,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if let threadId = threadId {
let source: ContextContentSource
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage(
peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
)), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil)
chatController.canReadHistory.set(false)
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
@ -1545,7 +1588,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
let source: ContextContentSource
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage(
peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
)), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil)
chatController.canReadHistory.set(false)
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))

View File

@ -3137,7 +3137,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
if case let .peer(peerData) = item.content, peerData.customMessageListData?.hidePeerStatus == true {
currentCredibilityIconContent = nil
} else if case .savedMessagesChats = item.chatListLocation, peer.id == item.context.account.peerId {
} else if case let .savedMessagesChats(peerId) = item.chatListLocation, peer.id == peerId {
currentCredibilityIconContent = nil
} else if peer.isScam {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
@ -3273,6 +3273,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
var isFirstForumThreadSelectable = false
var forumThreads: [(id: Int64, title: NSAttributedString, iconId: Int64?, iconColor: Int32)] = []
if case .savedMessagesChats = item.chatListLocation {
} else if case let .peer(peer) = item.content, case let .channel(channel) = peer.peer.peer, channel.flags.contains(.isMonoforum) {
} else if forumThread != nil || !topForumTopicItems.isEmpty {
if let forumThread = forumThread {
isFirstForumThreadSelectable = forumThread.isUnread

View File

@ -1535,9 +1535,9 @@ public final class ChatListNode: ListView {
}
}
}, setItemPinned: { [weak self] itemId, _ in
if case .savedMessagesChats = location {
if case let .savedMessagesChats(peerId) = location {
if case let .peer(itemPeerId) = itemId {
let _ = (context.engine.peers.toggleForumChannelTopicPinned(id: context.account.peerId, threadId: itemPeerId.toInt64())
let _ = (context.engine.peers.toggleForumChannelTopicPinned(id: peerId, threadId: itemPeerId.toInt64())
|> deliverOnMainQueue).start(error: { error in
guard let self else {
return

View File

@ -319,9 +319,9 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
isFirst = false
return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil)
}
case .savedMessagesChats:
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: account.peerId)
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: account.peerId)
case let .savedMessagesChats(peerId):
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId)
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: peerId)
var isFirst = true
return account.postbox.combinedView(keys: [viewKey, interfaceStateKey])

View File

@ -6833,16 +6833,17 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.messages {
static func getSavedDialogs(flags: Int32, offsetDate: Int32, offsetId: Int32, offsetPeer: Api.InputPeer, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SavedDialogs>) {
static func getSavedDialogs(flags: Int32, parentPeer: Api.InputPeer?, offsetDate: Int32, offsetId: Int32, offsetPeer: Api.InputPeer, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SavedDialogs>) {
let buffer = Buffer()
buffer.appendInt32(1401016858)
buffer.appendInt32(512883865)
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {parentPeer!.serialize(buffer, true)}
serializeInt32(offsetDate, buffer: buffer, boxed: false)
serializeInt32(offsetId, buffer: buffer, boxed: false)
offsetPeer.serialize(buffer, true)
serializeInt32(limit, buffer: buffer, boxed: false)
serializeInt64(hash, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getSavedDialogs", parameters: [("flags", String(describing: flags)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("offsetPeer", String(describing: offsetPeer)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SavedDialogs? in
return (FunctionDescription(name: "messages.getSavedDialogs", parameters: [("flags", String(describing: flags)), ("parentPeer", String(describing: parentPeer)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("offsetPeer", String(describing: offsetPeer)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SavedDialogs? in
let reader = BufferReader(buffer)
var result: Api.messages.SavedDialogs?
if let signature = reader.readInt32() {
@ -6868,9 +6869,11 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.messages {
static func getSavedHistory(peer: Api.InputPeer, offsetId: Int32, offsetDate: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
static func getSavedHistory(flags: Int32, parentPeer: Api.InputPeer?, peer: Api.InputPeer, offsetId: Int32, offsetDate: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
let buffer = Buffer()
buffer.appendInt32(1033519437)
buffer.appendInt32(-1718964215)
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {parentPeer!.serialize(buffer, true)}
peer.serialize(buffer, true)
serializeInt32(offsetId, buffer: buffer, boxed: false)
serializeInt32(offsetDate, buffer: buffer, boxed: false)
@ -6879,7 +6882,7 @@ public extension Api.functions.messages {
serializeInt32(maxId, buffer: buffer, boxed: false)
serializeInt32(minId, buffer: buffer, boxed: false)
serializeInt64(hash, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getSavedHistory", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
return (FunctionDescription(name: "messages.getSavedHistory", parameters: [("flags", String(describing: flags)), ("parentPeer", String(describing: parentPeer)), ("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
let reader = BufferReader(buffer)
var result: Api.messages.Messages?
if let signature = reader.readInt32() {

View File

@ -802,7 +802,7 @@ extension StoreMessage {
}
}
if peerId == accountPeerId, let savedPeerId = savedPeerId {
if let savedPeerId {
threadId = savedPeerId.peerId.toInt64()
}

View File

@ -684,218 +684,105 @@ public func _internal_fillSavedMessageHistory(accountPeerId: PeerId, postbox: Po
}
func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, query: String?, offsetIndex: StoredPeerThreadCombinedState.Index?, limit: Int) -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> {
if peerId == accountPeerId {
var flags: Int32 = 0
flags = 0
return postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
}
|> castError(LoadMessageHistoryThreadsError.self)
|> mapToSignal { peer -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> in
guard let peer else {
return .fail(.generic)
}
var offsetDate: Int32 = 0
var offsetId: Int32 = 0
var offsetPeer: Api.InputPeer = .inputPeerEmpty
if let offsetIndex = offsetIndex {
offsetDate = offsetIndex.timestamp
offsetId = offsetIndex.messageId
//TODO:api
offsetPeer = .inputPeerEmpty
var isSavedThreads = false
if peer.id == accountPeerId {
isSavedThreads = true
} else if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
isSavedThreads = true
}
let signal: Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> = network.request(Api.functions.messages.getSavedDialogs(
flags: flags,
offsetDate: offsetDate,
offsetId: offsetId,
offsetPeer: offsetPeer,
limit: Int32(limit),
hash: 0
))
|> `catch` { error -> Signal<Api.messages.SavedDialogs, LoadMessageHistoryThreadsError> in
if error.errorDescription == "SAVED_DIALOGS_UNSUPPORTED" {
return .never()
} else {
return .fail(.generic)
}
}
|> mapToSignal { result -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> in
switch result {
case .savedDialogs(let dialogs, let messages, let chats, let users), .savedDialogsSlice(_, let dialogs, let messages, let chats, let users):
var items: [LoadMessageHistoryThreadsResult.Item] = []
var pinnedIds: [Int64] = []
let addedMessages = messages.compactMap { message -> StoreMessage? in
return StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: false)
}
var minIndex: StoredPeerThreadCombinedState.Index?
for dialog in dialogs {
switch dialog {
case let .savedDialog(flags, peer, topMessage):
if (flags & (1 << 2)) != 0 {
pinnedIds.append(peer.peerId.toInt64())
}
let data = MessageHistoryThreadData(
creationDate: 0,
isOwnedByMe: true,
author: peer.peerId,
info: EngineMessageHistoryThread.Info(
title: "",
icon: nil,
iconColor: 0
),
incomingUnreadCount: 0,
maxIncomingReadId: 0,
maxKnownMessageId: topMessage,
maxOutgoingReadId: 0,
isClosed: false,
isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
)
var topTimestamp: Int32 = 1
for message in addedMessages {
if message.id.peerId == peerId && message.threadId == peer.peerId.toInt64() {
topTimestamp = max(topTimestamp, message.timestamp)
}
}
let topicIndex = StoredPeerThreadCombinedState.Index(timestamp: topTimestamp, threadId: peer.peerId.toInt64(), messageId: topMessage)
if let minIndexValue = minIndex {
if topicIndex < minIndexValue {
minIndex = topicIndex
}
} else {
minIndex = topicIndex
}
items.append(LoadMessageHistoryThreadsResult.Item(
threadId: peer.peerId.toInt64(),
data: data,
topMessage: topMessage,
unreadMentionsCount: 0,
unreadReactionsCount: 0,
index: topicIndex
))
}
}
var pinnedThreadIds: [Int64]?
if offsetIndex == nil {
pinnedThreadIds = pinnedIds
}
var nextIndex: StoredPeerThreadCombinedState.Index
if dialogs.count != 0 {
nextIndex = minIndex ?? StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
} else {
nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
}
if let offsetIndex = offsetIndex, nextIndex == offsetIndex {
nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
}
let combinedState = PeerThreadCombinedState(validIndexBoundary: nextIndex)
return .single(LoadMessageHistoryThreadsResult(
peerId: peerId,
items: items,
messages: addedMessages,
pinnedThreadIds: pinnedThreadIds,
combinedState: combinedState,
users: users,
chats: chats
))
case .savedDialogsNotModified:
return .complete()
}
}
return signal
} else {
let signal: Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> = postbox.transaction { transaction -> Api.InputChannel? in
guard let channel = transaction.getPeer(peerId) as? TelegramChannel else {
return nil
}
if !channel.flags.contains(.isForum) {
return nil
}
return apiInputChannel(channel)
}
|> castError(LoadMessageHistoryThreadsError.self)
|> mapToSignal { inputChannel -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> in
guard let inputChannel = inputChannel else {
return .fail(.generic)
}
if isSavedThreads {
var flags: Int32 = 0
if query != nil {
flags |= 1 << 0
}
flags = 0
var offsetDate: Int32 = 0
var offsetId: Int32 = 0
var offsetTopic: Int32 = 0
var offsetPeer: Api.InputPeer = .inputPeerEmpty
if let offsetIndex = offsetIndex {
offsetDate = offsetIndex.timestamp
offsetId = offsetIndex.messageId
offsetTopic = Int32(clamping: offsetIndex.threadId)
offsetPeer = .inputPeerEmpty
}
let signal: Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> = network.request(Api.functions.channels.getForumTopics(
var parentPeer: Api.InputPeer?
if peerId != accountPeerId {
guard let inputChannel = apiInputPeer(peer) else {
return .fail(.generic)
}
flags |= 1 << 1
parentPeer = inputChannel
}
let signal: Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> = network.request(Api.functions.messages.getSavedDialogs(
flags: flags,
channel: inputChannel,
q: query,
parentPeer: parentPeer,
offsetDate: offsetDate,
offsetId: offsetId,
offsetTopic: offsetTopic,
limit: Int32(limit)
offsetPeer: offsetPeer,
limit: Int32(limit),
hash: 0
))
|> mapError { _ -> LoadMessageHistoryThreadsError in
return .generic
|> `catch` { error -> Signal<Api.messages.SavedDialogs, LoadMessageHistoryThreadsError> in
if error.errorDescription == "SAVED_DIALOGS_UNSUPPORTED" {
return .never()
} else {
return .fail(.generic)
}
}
|> mapToSignal { result -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> in
switch result {
case let .forumTopics(_, _, topics, messages, chats, users, pts):
case .savedDialogs(let dialogs, let messages, let chats, let users), .savedDialogsSlice(_, let dialogs, let messages, let chats, let users):
var items: [LoadMessageHistoryThreadsResult.Item] = []
var pinnedIds: [Int64] = []
let addedMessages = messages.compactMap { message -> StoreMessage? in
return StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: true)
return StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: false)
}
let _ = pts
var minIndex: StoredPeerThreadCombinedState.Index?
for topic in topics {
switch topic {
case let .forumTopic(flags, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings, draft):
let _ = draft
if (flags & (1 << 3)) != 0 {
pinnedIds.append(Int64(id))
for dialog in dialogs {
switch dialog {
case let .savedDialog(flags, peer, topMessage):
if (flags & (1 << 2)) != 0 {
pinnedIds.append(peer.peerId.toInt64())
}
let data = MessageHistoryThreadData(
creationDate: date,
isOwnedByMe: (flags & (1 << 1)) != 0,
author: fromId.peerId,
creationDate: 0,
isOwnedByMe: true,
author: peer.peerId,
info: EngineMessageHistoryThread.Info(
title: title,
icon: iconEmojiId == 0 ? nil : iconEmojiId,
iconColor: iconColor
title: "",
icon: nil,
iconColor: 0
),
incomingUnreadCount: unreadCount,
maxIncomingReadId: readInboxMaxId,
incomingUnreadCount: 0,
maxIncomingReadId: 0,
maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId,
isClosed: (flags & (1 << 2)) != 0,
isHidden: (flags & (1 << 6)) != 0,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
maxOutgoingReadId: 0,
isClosed: false,
isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
)
var topTimestamp = date
var topTimestamp: Int32 = 1
for message in addedMessages {
if message.id.peerId == peerId && message.threadId == Int64(id) {
if message.id.peerId == peerId && message.threadId == peer.peerId.toInt64() {
topTimestamp = max(topTimestamp, message.timestamp)
}
}
let topicIndex = StoredPeerThreadCombinedState.Index(timestamp: topTimestamp, threadId: Int64(id), messageId: topMessage)
let topicIndex = StoredPeerThreadCombinedState.Index(timestamp: topTimestamp, threadId: peer.peerId.toInt64(), messageId: topMessage)
if let minIndexValue = minIndex {
if topicIndex < minIndexValue {
minIndex = topicIndex
@ -905,15 +792,13 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
}
items.append(LoadMessageHistoryThreadsResult.Item(
threadId: Int64(id),
threadId: peer.peerId.toInt64(),
data: data,
topMessage: topMessage,
unreadMentionsCount: unreadMentionsCount,
unreadReactionsCount: unreadReactionsCount,
unreadMentionsCount: 0,
unreadReactionsCount: 0,
index: topicIndex
))
case .forumTopicDeleted:
break
}
}
@ -923,7 +808,7 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
}
var nextIndex: StoredPeerThreadCombinedState.Index
if topics.count != 0 {
if dialogs.count != 0 {
nextIndex = minIndex ?? StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
} else {
nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
@ -943,12 +828,154 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
users: users,
chats: chats
))
case .savedDialogsNotModified:
return .complete()
}
}
return signal
} else {
let signal: Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> = postbox.transaction { transaction -> Api.InputChannel? in
guard let channel = transaction.getPeer(peerId) as? TelegramChannel else {
return nil
}
if !channel.flags.contains(.isForum) {
return nil
}
return apiInputChannel(channel)
}
|> castError(LoadMessageHistoryThreadsError.self)
|> mapToSignal { inputChannel -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> in
guard let inputChannel = inputChannel else {
return .fail(.generic)
}
var flags: Int32 = 0
if query != nil {
flags |= 1 << 0
}
var offsetDate: Int32 = 0
var offsetId: Int32 = 0
var offsetTopic: Int32 = 0
if let offsetIndex = offsetIndex {
offsetDate = offsetIndex.timestamp
offsetId = offsetIndex.messageId
offsetTopic = Int32(clamping: offsetIndex.threadId)
}
let signal: Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> = network.request(Api.functions.channels.getForumTopics(
flags: flags,
channel: inputChannel,
q: query,
offsetDate: offsetDate,
offsetId: offsetId,
offsetTopic: offsetTopic,
limit: Int32(limit)
))
|> mapError { _ -> LoadMessageHistoryThreadsError in
return .generic
}
|> mapToSignal { result -> Signal<LoadMessageHistoryThreadsResult, LoadMessageHistoryThreadsError> in
switch result {
case let .forumTopics(_, _, topics, messages, chats, users, pts):
var items: [LoadMessageHistoryThreadsResult.Item] = []
var pinnedIds: [Int64] = []
let addedMessages = messages.compactMap { message -> StoreMessage? in
return StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: true)
}
let _ = pts
var minIndex: StoredPeerThreadCombinedState.Index?
for topic in topics {
switch topic {
case let .forumTopic(flags, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings, draft):
let _ = draft
if (flags & (1 << 3)) != 0 {
pinnedIds.append(Int64(id))
}
let data = MessageHistoryThreadData(
creationDate: date,
isOwnedByMe: (flags & (1 << 1)) != 0,
author: fromId.peerId,
info: EngineMessageHistoryThread.Info(
title: title,
icon: iconEmojiId == 0 ? nil : iconEmojiId,
iconColor: iconColor
),
incomingUnreadCount: unreadCount,
maxIncomingReadId: readInboxMaxId,
maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId,
isClosed: (flags & (1 << 2)) != 0,
isHidden: (flags & (1 << 6)) != 0,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
)
var topTimestamp = date
for message in addedMessages {
if message.id.peerId == peerId && message.threadId == Int64(id) {
topTimestamp = max(topTimestamp, message.timestamp)
}
}
let topicIndex = StoredPeerThreadCombinedState.Index(timestamp: topTimestamp, threadId: Int64(id), messageId: topMessage)
if let minIndexValue = minIndex {
if topicIndex < minIndexValue {
minIndex = topicIndex
}
} else {
minIndex = topicIndex
}
items.append(LoadMessageHistoryThreadsResult.Item(
threadId: Int64(id),
data: data,
topMessage: topMessage,
unreadMentionsCount: unreadMentionsCount,
unreadReactionsCount: unreadReactionsCount,
index: topicIndex
))
case .forumTopicDeleted:
break
}
}
var pinnedThreadIds: [Int64]?
if offsetIndex == nil {
pinnedThreadIds = pinnedIds
}
var nextIndex: StoredPeerThreadCombinedState.Index
if topics.count != 0 {
nextIndex = minIndex ?? StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
} else {
nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
}
if let offsetIndex = offsetIndex, nextIndex == offsetIndex {
nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)
}
let combinedState = PeerThreadCombinedState(validIndexBoundary: nextIndex)
return .single(LoadMessageHistoryThreadsResult(
peerId: peerId,
items: items,
messages: addedMessages,
pinnedThreadIds: pinnedThreadIds,
combinedState: combinedState,
users: users,
chats: chats
))
}
}
return signal
}
return signal
}
return signal
}
}

View File

@ -2161,16 +2161,24 @@ public final class AccountViewTracker {
if let account = self.account {
let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>
if let peerId = chatLocation.peerId, let threadId = chatLocation.threadId, tag == nil {
signal = account.postbox.transaction { transaction -> (MessageHistoryThreadData?, MessageIndex?) in
signal = account.postbox.transaction { transaction -> (Peer?, MessageHistoryThreadData?, MessageIndex?) in
let interfaceState = transaction.getPeerChatThreadInterfaceState(peerId, threadId: threadId)
return (
transaction.getPeer(peerId),
transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self),
interfaceState?.historyScrollMessageIndex
)
}
|> mapToSignal { threadInfo, scrollRestorationIndex -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in
|> mapToSignal { peer, threadInfo, scrollRestorationIndex -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in
var isSimpleThread = false
if peerId == account.peerId {
isSimpleThread = true
} else if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
isSimpleThread = true
}
if isSimpleThread {
let anchor: HistoryViewInputAnchor
if let scrollRestorationIndex {
anchor = .index(scrollRestorationIndex)

View File

@ -339,9 +339,12 @@ enum FetchMessageHistoryHoleThreadInput: CustomStringConvertible {
}
}
func requestThreadId(accountPeerId: PeerId) -> Int64? {
func requestThreadId(accountPeerId: PeerId, peer: Peer) -> Int64? {
switch self {
case let .direct(peerId, threadId):
if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
return nil
}
if let threadId = threadId, peerId != accountPeerId {
return threadId
} else {
@ -352,10 +355,12 @@ enum FetchMessageHistoryHoleThreadInput: CustomStringConvertible {
}
}
func requestSubPeerId(accountPeerId: PeerId) -> PeerId? {
func requestSubPeerId(accountPeerId: PeerId, peer: Peer) -> PeerId? {
switch self {
case let .direct(peerId, threadId):
if let threadId = threadId, peerId == accountPeerId {
if let threadId, peerId == accountPeerId {
return PeerId(threadId)
} else if let threadId, let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
return PeerId(threadId)
} else {
return nil
@ -390,7 +395,12 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
return postbox.transaction { transaction -> (Peer?, Int64, Peer?) in
switch peerInput {
case let .direct(peerId, _):
return (transaction.getPeer(peerId), 0, peerInput.requestSubPeerId(accountPeerId: accountPeerId).flatMap(transaction.getPeer))
let peer = transaction.getPeer(peerId)
var subPeerId: PeerId?
if let peer {
subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId, peer: peer)
}
return (peer, 0, subPeerId.flatMap(transaction.getPeer))
case let .threadFromChannel(channelMessageId):
return (transaction.getPeer(channelMessageId.peerId), 0, nil)
}
@ -411,7 +421,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
switch space {
case .everywhere:
if let requestThreadId = peerInput.requestThreadId(accountPeerId: accountPeerId) {
if let requestThreadId = peerInput.requestThreadId(accountPeerId: accountPeerId, peer: peer) {
let offsetId: Int32
let addOffset: Int32
let selectedLimit = count
@ -459,7 +469,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
}
request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: Int32(clamping: requestThreadId), offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
} else if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId) {
} else if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId, peer: peer) {
guard let subPeer, subPeer.id == subPeerId, let inputSubPeer = apiInputPeer(subPeer) else {
Logger.shared.log("fetchMessageHistoryHole", "subPeer not available")
return .never()
@ -511,7 +521,14 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
minMaxRange = 1 ... (Int32.max - 1)
}
request = source.request(Api.functions.messages.getSavedHistory(peer: inputSubPeer, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
var getSavedHistoryFlags: Int32 = 0
var parentPeer: Api.InputPeer?
if peer.id != accountPeerId {
getSavedHistoryFlags |= 1 << 0
parentPeer = inputPeer
}
request = source.request(Api.functions.messages.getSavedHistory(flags: getSavedHistoryFlags, parentPeer: parentPeer, peer: inputSubPeer, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
} else {
let offsetId: Int32
let addOffset: Int32
@ -611,7 +628,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId) {
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId, peer: peer) {
flags |= (1 << 1)
topMsgId = Int32(clamping: threadId)
}
@ -666,7 +683,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId) {
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId, peer: peer) {
flags |= (1 << 0)
topMsgId = Int32(clamping: threadId)
}
@ -730,13 +747,13 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId) {
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId, peer: peer) {
flags |= (1 << 1)
topMsgId = Int32(clamping: threadId)
}
var savedPeerId: Api.InputPeer?
if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId), let subPeer = subPeer, subPeer.id == subPeerId {
if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId, peer: peer), let subPeer = subPeer, subPeer.id == subPeerId {
if let inputPeer = apiInputPeer(subPeer) {
flags |= 1 << 2
savedPeerId = inputPeer
@ -799,13 +816,13 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId) {
if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId, peer: peer) {
flags |= (1 << 1)
topMsgId = Int32(clamping: threadId)
}
var savedPeerId: Api.InputPeer?
if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId), let subPeer = subPeer, subPeer.id == subPeerId {
if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId, peer: peer), let subPeer = subPeer, subPeer.id == subPeerId {
if let inputPeer = apiInputPeer(subPeer) {
flags |= 1 << 2
savedPeerId = inputPeer
@ -960,9 +977,6 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
}
print("fetchMessageHistoryHole for \(peerInput) space \(space) done")
if peerInput.requestThreadId(accountPeerId: accountPeerId) != nil, case .everywhere = space, case .aroundId = direction {
assert(true)
}
if ids.count == 0 || implicitelyFillHole {
filledRange = minMaxRange
@ -974,7 +988,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound)
strictFilledIndices = IndexSet(integersIn: Int(min(aroundId.id, messageRange.lowerBound)) ... Int(max(aroundId.id, messageRange.upperBound)))
var shouldFillAround = false
if peerInput.requestThreadId(accountPeerId: accountPeerId) != nil {
if peerInput.requestThreadId(accountPeerId: accountPeerId, peer: peer) != nil || peerInput.requestSubPeerId(accountPeerId: accountPeerId, peer: peer) != nil {
shouldFillAround = true
}
if case .customTag = space {

View File

@ -565,6 +565,7 @@ public struct ChatReplyThreadMessage: Equatable {
public var channelMessageId: MessageId?
public var isChannelPost: Bool
public var isForumPost: Bool
public var isMonoforum: Bool
public var maxMessage: MessageId?
public var maxReadIncomingMessageId: MessageId?
public var maxReadOutgoingMessageId: MessageId?
@ -581,12 +582,13 @@ public struct ChatReplyThreadMessage: Equatable {
}
}
public init(peerId: PeerId, threadId: Int64, channelMessageId: MessageId?, isChannelPost: Bool, isForumPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) {
public init(peerId: PeerId, threadId: Int64, channelMessageId: MessageId?, isChannelPost: Bool, isForumPost: Bool, isMonoforum: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) {
self.peerId = peerId
self.threadId = threadId
self.channelMessageId = channelMessageId
self.isChannelPost = isChannelPost
self.isForumPost = isForumPost
self.isMonoforum = isMonoforum
self.maxMessage = maxMessage
self.maxReadIncomingMessageId = maxReadIncomingMessageId
self.maxReadOutgoingMessageId = maxReadOutgoingMessageId
@ -598,7 +600,7 @@ public struct ChatReplyThreadMessage: Equatable {
public var normalized: ChatReplyThreadMessage {
if self.isForumPost {
return ChatReplyThreadMessage(peerId: self.peerId, threadId: self.threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)
return ChatReplyThreadMessage(peerId: self.peerId, threadId: self.threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)
} else {
return self
}
@ -931,6 +933,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
channelMessageId: discussionMessage.channelMessageId,
isChannelPost: discussionMessage.isChannelPost,
isForumPost: discussionMessage.isForumPost,
isMonoforum: false,
maxMessage: discussionMessage.maxMessage,
maxReadIncomingMessageId: discussionMessage.maxReadIncomingMessageId,
maxReadOutgoingMessageId: discussionMessage.maxReadOutgoingMessageId,

View File

@ -827,7 +827,13 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre
guard let subPeer = transaction.getPeer(PeerId(threadId)), let inputSubPeer = apiInputPeer(subPeer) else {
return .single(nil)
}
let primaryIndex = account.network.request(Api.functions.messages.getSavedHistory(peer: inputSubPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0))
var getSavedHistoryFlags: Int32 = 0
var parentPeer: Api.InputPeer?
if peer.id != account.peerId {
getSavedHistoryFlags |= 1 << 0
parentPeer = inputPeer
}
let primaryIndex = account.network.request(Api.functions.messages.getSavedHistory(flags: getSavedHistoryFlags, parentPeer: parentPeer, peer: inputSubPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0))
|> map { result -> MessageIndex? in
let messages: [Api.Message]
switch result {

View File

@ -29,6 +29,9 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId:
return .single(nil)
}
}
if peer.flags.contains(.isMonoforum) {
return .single(nil)
}
let apiFilter: Api.ChannelParticipantsFilter
switch category {

View File

@ -30,6 +30,10 @@ func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountP
} else {
peer = rawPeer
}
if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
return .single(false)
}
let cachedData = transaction.getPeerCachedData(peerId: peer.id)
@ -579,6 +583,9 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
}
}
} else if let inputChannel = maybePeer.flatMap(apiInputChannel) {
if let channel = maybePeer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
return .single(false)
}
let fullChannelSignal = network.request(Api.functions.channels.getFullChannel(channel: inputChannel))
|> map(Optional.init)
|> `catch` { error -> Signal<Api.messages.ChatFull?, NoError> in

View File

@ -804,7 +804,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
return ChatListItem(
presentationData: chatListPresentationData,
context: component.context,
chatListLocation: component.peerId == component.context.account.peerId ? .savedMessagesChats : .chatList(groupId: .root),
chatListLocation: component.peerId == component.context.account.peerId ? .savedMessagesChats(peerId: component.context.account.peerId) : .chatList(groupId: .root),
filterData: nil,
index: .forum(
pinnedIndex: .none,

View File

@ -149,7 +149,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS
self.chatListNode = ChatListNode(
context: self.context,
location: .savedMessagesChats,
location: .savedMessagesChats(peerId: context.account.peerId),
chatListFilter: nil,
previewing: false,
fillPreloadItems: false,
@ -210,6 +210,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS
channelMessageId: nil,
isChannelPost: false,
isForumPost: false,
isMonoforum: false,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
@ -378,7 +379,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS
if case let .peer(peerData) = item.content {
let threadId = peerData.peer.peerId.toInt64()
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .replyThread(message: ChatReplyThreadMessage(
peerId: self.context.account.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
peerId: self.context.account.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: false, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
)), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil)
chatController.canReadHistory.set(false)
let source: ContextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: parentController.navigationController as? NavigationController))

View File

@ -149,7 +149,7 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro
self.coveringView = UIView()
self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .replyThread(message: ChatReplyThreadMessage(peerId: context.account.peerId, threadId: peerId.toInt64(), channelMessageId: nil, isChannelPost: false, isForumPost: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)), subject: nil, botStart: nil, mode: .standard(.embedded(invertDirection: true)), params: nil)
self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .replyThread(message: ChatReplyThreadMessage(peerId: context.account.peerId, threadId: peerId.toInt64(), channelMessageId: nil, isChannelPost: false, isForumPost: false, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)), subject: nil, botStart: nil, mode: .standard(.embedded(invertDirection: true)), params: nil)
self.chatController.navigation_setNavigationController(navigationController())
super.init()

View File

@ -11016,7 +11016,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self?.deactivateSearch()
})
} else if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .savedMessagesChats = currentPaneKey {
let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, filter: [.removeSearchHeader], requestPeerType: nil, location: .savedMessagesChats, displaySearchFilters: false, hasDownloads: false, initialFilter: .chats, openPeer: { [weak self] peer, _, _, _ in
let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, filter: [.removeSearchHeader], requestPeerType: nil, location: .savedMessagesChats(peerId: self.context.account.peerId), displaySearchFilters: false, hasDownloads: false, initialFilter: .chats, openPeer: { [weak self] peer, _, _, _ in
guard let self else {
return
}
@ -11032,6 +11032,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
channelMessageId: nil,
isChannelPost: false,
isForumPost: false,
isMonoforum: false,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
@ -11061,6 +11062,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
channelMessageId: nil,
isChannelPost: false,
isForumPost: false,
isMonoforum: false,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
@ -13374,7 +13376,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
let navigateChatLocation: NavigateToChatControllerParams.Location
if let threadId = item.threadId {
navigateChatLocation = .replyThread(ChatReplyThreadMessage(
peerId: item.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
peerId: item.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false,maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
))
} else {
navigateChatLocation = .peer(itemPeer)

View File

@ -2586,7 +2586,7 @@ final class StorageUsageScreenComponent: Component {
var chatLocation: NavigateToChatControllerParams.Location = .peer(peer)
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
}
component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
@ -2689,7 +2689,7 @@ final class StorageUsageScreenComponent: Component {
var chatLocation: NavigateToChatControllerParams.Location = .peer(peer)
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
}
component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(

View File

@ -325,7 +325,7 @@ final class AuthorizedApplicationContext {
let chatLocation: NavigateToChatControllerParams.Location
if let _ = threadData, let threadId = firstMessage.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(
peerId: firstMessage.id.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
peerId: firstMessage.id.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
).normalized)
} else {
guard let peer = firstMessage.peers[firstMessage.id.peerId] else {
@ -941,7 +941,7 @@ final class AuthorizedApplicationContext {
let chatLocation: NavigateToChatControllerParams.Location
if let threadId = threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(
peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
))
} else {
chatLocation = .peer(peer)

View File

@ -6231,14 +6231,23 @@ extension ChatControllerImpl {
guard let peerId = self.chatLocation.peerId else {
return
}
guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else {
return
}
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,
isMonoforum: isMonoforum,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
@ -6709,6 +6718,7 @@ extension ChatControllerImpl {
channelMessageId: nil,
isChannelPost: false,
isForumPost: true,
isMonoforum: false,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,

View File

@ -144,7 +144,7 @@ extension ChatControllerImpl {
let navigateToLocation: NavigateToChatControllerParams.Location
if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) {
navigateToLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
navigateToLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false,maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
} else {
navigateToLocation = .peer(peer)
}
@ -179,7 +179,7 @@ extension ChatControllerImpl {
var displayMessageNotFoundToast = false
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
if let message = message, let threadId = message.threadId {
let replyThreadMessage = ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)
let replyThreadMessage = ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)
chatLocation = .replyThread(replyThreadMessage)
preloadChatLocation = .replyThread(message: replyThreadMessage)
} else {

View File

@ -6131,7 +6131,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let chatLocation: ChatLocation
if let threadId {
chatLocation = .replyThread(message: ChatReplyThreadMessage(peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
chatLocation = .replyThread(message: ChatReplyThreadMessage(peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
} else {
chatLocation = .peer(id: peerId)
}

View File

@ -2964,6 +2964,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
channelMessageId: nil,
isChannelPost: false,
isForumPost: false,
isMonoforum: false,
maxMessage: nil,
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,

View File

@ -1647,8 +1647,18 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
let topicAuthorId: Signal<EnginePeer.Id?, NoError>
if let peerId = chatLocation.peerId, let threadId = chatLocation.threadId {
topicAuthorId = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId))
|> map { data -> EnginePeer.Id? in
topicAuthorId = context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId)
)
|> map { peer, data -> EnginePeer.Id? in
guard let peer else {
return nil
}
if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
return nil
}
return data?.author
}
|> distinctUntilChanged

View File

@ -102,7 +102,7 @@ func chatHistoryViewForLocation(
if tag != nil {
requestAroundId = true
}
if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId {
if case let .replyThread(message) = chatLocation, (message.peerId == context.account.peerId || message.isMonoforum) {
preFixedReadState = .peer([:])
}

View File

@ -185,7 +185,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe
self.searchState = searchState
if case let .peer(peerId, _, _, _, _, _, _) = location, peerId == context.account.peerId {
self.mappedLocation = .savedMessagesChats
self.mappedLocation = .savedMessagesChats(peerId: peerId)
} else {
self.mappedLocation = .chatList(groupId: .root)
}

View File

@ -27,7 +27,9 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
}
var viewForumAsMessages: Signal<Bool, NoError> = .single(false)
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
viewForumAsMessages = .single(false)
} else if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
viewForumAsMessages = params.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)])
|> take(1)
|> map { combinedView in
@ -52,6 +54,11 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
let _ = (viewForumAsMessages
|> take(1)
|> deliverOnMainQueue).start(next: { viewForumAsMessages in
var viewForumAsMessages = viewForumAsMessages
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isMonoforum), channel.adminRights == nil {
viewForumAsMessages = true
}
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum), !viewForumAsMessages {
for controller in params.navigationController.viewControllers.reversed() {
var chatListController: ChatListControllerImpl?
@ -65,6 +72,8 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
var matches = false
if case let .forum(peerId) = chatListController.location, peer.id == peerId {
matches = true
} else if case let .savedMessagesChats(peerId) = chatListController.location, peer.id == peerId {
matches = true
} else if case let .forum(peerId) = chatListController.effectiveLocation, peer.id == peerId {
matches = true
}
@ -79,7 +88,14 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
}
}
let controller = ChatListControllerImpl(context: params.context, location: .forum(peerId: peer.id), controlsHistoryPreload: false, enableDebugActions: false)
let chatListLocation: ChatListControllerLocation
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
chatListLocation = .savedMessagesChats(peerId: peer.id)
} else {
chatListLocation = .forum(peerId: peer.id)
}
let controller = ChatListControllerImpl(context: params.context, location: chatListLocation, controlsHistoryPreload: false, enableDebugActions: false)
let activateMessageSearch = params.activateMessageSearch
let chatListCompletion = params.chatListCompletion

View File

@ -76,10 +76,14 @@ public struct ChatTranslationState: Codable {
}
private func cachedChatTranslationState(engine: TelegramEngine, peerId: EnginePeer.Id, threadId: Int64?) -> Signal<ChatTranslationState?, NoError> {
let key = EngineDataBuffer(length: 8)
key.setInt64(0, value: peerId.id._internalGetInt64Value())
let key: EngineDataBuffer
if let threadId {
key = EngineDataBuffer(length: 8 + 8)
key.setInt64(0, value: peerId.id._internalGetInt64Value())
key.setInt64(8, value: threadId)
} else {
key = EngineDataBuffer(length: 8)
key.setInt64(0, value: peerId.id._internalGetInt64Value())
}
return engine.data.subscribe(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.translationState, id: key))
@ -89,10 +93,14 @@ private func cachedChatTranslationState(engine: TelegramEngine, peerId: EnginePe
}
private func updateChatTranslationState(engine: TelegramEngine, peerId: EnginePeer.Id, threadId: Int64?, state: ChatTranslationState?) -> Signal<Never, NoError> {
let key = EngineDataBuffer(length: 8)
key.setInt64(0, value: peerId.id._internalGetInt64Value())
let key: EngineDataBuffer
if let threadId {
key = EngineDataBuffer(length: 8 + 8)
key.setInt64(0, value: peerId.id._internalGetInt64Value())
key.setInt64(8, value: threadId)
} else {
key = EngineDataBuffer(length: 8)
key.setInt64(0, value: peerId.id._internalGetInt64Value())
}
if let state {

View File

@ -841,7 +841,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .progress
case let .result(info):
if let _ = info {
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
} else {
return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
}
@ -866,7 +866,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .progress
case let .result(info):
if let _ = info {
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(replyThreadMessageId.id), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId)))
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(replyThreadMessageId.id), channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId)))
} else {
return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
}
@ -956,7 +956,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .progress
case let .result(info):
if let _ = info {
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(threadId), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(threadId), channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
} else {
return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
}
@ -980,7 +980,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
return .progress
case let .result(info):
if let _ = info {
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, isMonoforum: false, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId))
} else {
return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
}