mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Temp
This commit is contained in:
parent
8843fbe5cb
commit
b4a0e8809c
21 changed files with 746 additions and 301 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
})
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1027,144 +1027,169 @@ 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)
|
||||
}
|
||||
subject = nil
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>, updatedPeers: [PeerId: Peer], 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,12 @@ final class ChatListTable: Table {
|
|||
for peerId in updatedChatListInclusions.keys {
|
||||
changedPeerIds.insert(peerId)
|
||||
}
|
||||
/*for peerId in updatedPeers.keys {
|
||||
changedPeerIds.insert(peerId)
|
||||
if let peer = postbox.peerTable.get(peerId), let targetPeerId = self.seedConfiguration.chatListPeerMergeIntoTargetId(peer) {
|
||||
changedPeerIds.insert(targetPeerId)
|
||||
}
|
||||
}*/
|
||||
|
||||
self.ensureInitialized(groupId: .root)
|
||||
|
||||
|
|
@ -396,8 +402,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 +419,22 @@ final class ChatListTable: Table {
|
|||
rawTopMessageIndex = nil
|
||||
}
|
||||
|
||||
/*for associatedId in postbox.reverseAssociatedPeerTable.get(peerId: peerId) {
|
||||
if let peer = postbox.peerTable.get(associatedId), let targetPeerId = self.seedConfiguration.chatListPeerMergeIntoTargetId(peer), targetPeerId == peerId {
|
||||
if let associatedTopMessage = messageHistoryTable.topIndex(peerId: associatedId) {
|
||||
if let currentTopMessageIndex = topMessage {
|
||||
if associatedTopMessage > currentTopMessageIndex {
|
||||
topMessageIndex = associatedTopMessage
|
||||
rawTopMessageIndex = associatedTopMessage
|
||||
}
|
||||
} else {
|
||||
topMessageIndex = associatedTopMessage
|
||||
rawTopMessageIndex = associatedTopMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
var updatedIndex = self.indexTable.setTopMessageIndex(peerId: peerId, index: topMessageIndex)
|
||||
if let updatedInclusion = updatedChatListInclusions[peerId] {
|
||||
updatedIndex = self.indexTable.setInclusion(peerId: peerId, inclusion: updatedInclusion)
|
||||
|
|
|
|||
|
|
@ -1040,19 +1040,69 @@ public final class ChatListView {
|
|||
self.groupId = mutableView.groupId
|
||||
|
||||
var entries: [ChatListEntry] = []
|
||||
|
||||
//TODO:release
|
||||
var linkedEntries: [PeerId: [MutableChatListEntry.MessageEntryData]] = [:]
|
||||
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 > index {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ public final class SeedConfiguration {
|
|||
public let automaticThreadIndexInfo: (PeerId, Int64) -> StoredMessageHistoryThreadInfo?
|
||||
public let customTagsFromAttributes: ([MessageAttribute]) -> [MemoryBuffer]
|
||||
public let displaySavedMessagesAsTopicListPreferencesKey: ValueBoxKey
|
||||
public let chatListPeerMergeIntoTargetId: (Peer) -> PeerId?
|
||||
|
||||
public init(
|
||||
globalMessageIdsPeerIdNamespaces: Set<GlobalMessageIdsNamespace>,
|
||||
|
|
@ -111,7 +112,8 @@ public final class SeedConfiguration {
|
|||
isPeerUpgradeMessage: @escaping (Message) -> Bool,
|
||||
automaticThreadIndexInfo: @escaping (PeerId, Int64) -> StoredMessageHistoryThreadInfo?,
|
||||
customTagsFromAttributes: @escaping ([MessageAttribute]) -> [MemoryBuffer],
|
||||
displaySavedMessagesAsTopicListPreferencesKey: ValueBoxKey
|
||||
displaySavedMessagesAsTopicListPreferencesKey: ValueBoxKey,
|
||||
chatListPeerMergeIntoTargetId: @escaping (Peer) -> PeerId?
|
||||
) {
|
||||
self.globalMessageIdsPeerIdNamespaces = globalMessageIdsPeerIdNamespaces
|
||||
self.initializeChatListWithHole = initializeChatListWithHole
|
||||
|
|
@ -138,5 +140,6 @@ public final class SeedConfiguration {
|
|||
self.automaticThreadIndexInfo = automaticThreadIndexInfo
|
||||
self.customTagsFromAttributes = customTagsFromAttributes
|
||||
self.displaySavedMessagesAsTopicListPreferencesKey = displaySavedMessagesAsTopicListPreferencesKey
|
||||
self.chatListPeerMergeIntoTargetId = chatListPeerMergeIntoTargetId
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,10 +76,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) {
|
||||
|
|
@ -241,7 +245,13 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
|||
|
||||
return result
|
||||
},
|
||||
displaySavedMessagesAsTopicListPreferencesKey: PreferencesKeys.displaySavedChatsAsTopics()
|
||||
displaySavedMessagesAsTopicListPreferencesKey: PreferencesKeys.displaySavedChatsAsTopics(),
|
||||
chatListPeerMergeIntoTargetId: { peer in
|
||||
if let peer = peer as? TelegramChannel, let linkedBotId = peer.linkedBotId {
|
||||
return linkedBotId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
)
|
||||
}()
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -1144,12 +1150,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 +1170,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 +1245,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 +1301,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 +1321,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 +1383,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 +1400,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 +1450,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,18 +1682,62 @@ 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 {
|
||||
continue
|
||||
}
|
||||
self.rawItems.append(Item(item: item))
|
||||
}
|
||||
|
||||
|
|
@ -1786,7 +1859,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 +1879,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 +1953,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 +1969,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 +2012,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 +2039,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 +2086,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 +2118,7 @@ public final class ChatSideTopicsPanel: Component {
|
|||
|
||||
let _ = (chatForumTopicMenuItems(
|
||||
context: component.context,
|
||||
peerId: component.peerId,
|
||||
peerId: peerId,
|
||||
threadId: topicId,
|
||||
isPinned: isPinned,
|
||||
isClosed: isClosed,
|
||||
|
|
@ -2052,12 +2127,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 +2141,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 +2149,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 +2174,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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -166,6 +166,10 @@ extension ChatControllerImpl {
|
|||
if let historyNodeData = contentData.state.historyNodeData {
|
||||
self.updateChatLocationToOther(chatLocation: historyNodeData.chatLocation)
|
||||
return
|
||||
} else if case let .botForumThread(_, threadId) = self.subject {
|
||||
self.subject = nil
|
||||
self.updateChatLocationThread(threadId: threadId)
|
||||
return
|
||||
}
|
||||
|
||||
apply({ [weak self, weak contentData] forceAnimation in
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
@ -5528,6 +5528,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)
|
||||
}
|
||||
|
|
@ -8827,7 +8829,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()
|
||||
|
|
@ -10114,90 +10116,124 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
|
||||
public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) {
|
||||
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 {
|
||||
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 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 {
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.currentChatSwitchDirection = animationDirection
|
||||
self.chatLocation = updatedChatLocation
|
||||
historyNode.areContentAnimationsEnabled = true
|
||||
self.chatDisplayNode.prepareSwitchToChatLocation(historyNode: historyNode, animationDirection: animationDirection)
|
||||
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
|
||||
}
|
||||
|
||||
apply(true)
|
||||
self.saveInterfaceState()
|
||||
|
||||
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.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.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
|
||||
})
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue