This commit is contained in:
Isaac 2025-05-20 17:25:39 +08:00
parent f7db73db40
commit 109fa6b537
23 changed files with 962 additions and 622 deletions

View File

@ -1785,8 +1785,10 @@ ios_application(
#"//submodules/TelegramApi", #"//submodules/TelegramApi",
#"//submodules/TelegramCore", #"//submodules/TelegramCore",
#"//submodules/FFMpegBinding", #"//submodules/FFMpegBinding",
"//submodules/Display", #"//submodules/Display",
#"//third-party/webrtc", #"//third-party/webrtc",
"//submodules/AsyncDisplayKit",
"//submodules/ObjCRuntimeUtils",
], ],
) )

View File

@ -95,6 +95,7 @@ _IGNORE_OBJC_LIBRARY_ATTRS = [
"toolchains", "toolchains",
"transitive_configs", "transitive_configs",
"visibility", "visibility",
"package_metadata",
] ]
_IGNORE_OBJC_LIBRARY_EMPTY_ATTRS = [ _IGNORE_OBJC_LIBRARY_EMPTY_ATTRS = [
@ -106,6 +107,8 @@ _IGNORE_OBJC_LIBRARY_EMPTY_ATTRS = [
"restricted_to", "restricted_to",
"textual_hdrs", "textual_hdrs",
"sdk_includes", "sdk_includes",
"conlyopts",
"cxxopts",
] ]
_OBJC_LIBRARY_ATTRS = { _OBJC_LIBRARY_ATTRS = {
@ -154,6 +157,8 @@ _IGNORE_SWIFT_LIBRARY_ATTRS = [
"toolchains", "toolchains",
"transitive_configs", "transitive_configs",
"visibility", "visibility",
"library_evolution",
"package_metadata",
] ]
_IGNORE_SWIFT_LIBRARY_EMPTY_ATTRS = [ _IGNORE_SWIFT_LIBRARY_EMPTY_ATTRS = [
@ -332,9 +337,9 @@ def _collect_spm_modules_impl(target, ctx):
# Extract the path from the label # Extract the path from the label
# Example: @//path/ModuleName:ModuleSubname -> path/ModuleName # Example: @//path/ModuleName:ModuleSubname -> path/ModuleName
if not str(ctx.label).startswith("@//"): if not str(ctx.label).startswith("@@//"):
fail("Invalid label: {}".format(ctx.label)) fail("Invalid label: {}".format(ctx.label))
module_path = str(ctx.label).split(":")[0].split("@//")[1] module_path = str(ctx.label).split(":")[0].split("@@//")[1]
if module_type == "objc_library": if module_type == "objc_library":
module_info = { module_info = {

View File

@ -51,6 +51,11 @@ class EncryptionV2
key = keyIv[0..31] key = keyIv[0..31]
iv = keyIv[32..43] iv = keyIv[32..43]
auth_data = keyIv[44..-1] auth_data = keyIv[44..-1]
puts "key: #{key.inspect}"
puts "iv: #{iv.inspect}"
puts "auth_data: #{auth_data.inspect}"
cipher.key = key cipher.key = key
cipher.iv = iv cipher.iv = iv
cipher.auth_data = auth_data cipher.auth_data = auth_data

View File

@ -1073,6 +1073,7 @@ public protocol SharedAccountContext: AnyObject {
selectedMessages: Signal<Set<MessageId>?, NoError>, selectedMessages: Signal<Set<MessageId>?, NoError>,
mode: ChatHistoryListMode mode: ChatHistoryListMode
) -> ChatHistoryListNode ) -> ChatHistoryListNode
func subscribeChatListData(context: AccountContext, location: ChatListControllerLocation) -> Signal<EngineChatList, NoError>
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: ((UIView?, CGPoint?) -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: ((UIView?, CGPoint?) -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader

View File

@ -6,12 +6,12 @@ import Display
import TelegramUIPreferences import TelegramUIPreferences
import AccountContext import AccountContext
enum ChatListNodeLocation: Equatable { public enum ChatListNodeLocation: Equatable {
case initial(count: Int, filter: ChatListFilter?) case initial(count: Int, filter: ChatListFilter?)
case navigation(index: EngineChatList.Item.Index, filter: ChatListFilter?) case navigation(index: EngineChatList.Item.Index, filter: ChatListFilter?)
case scroll(index: EngineChatList.Item.Index, sourceIndex: EngineChatList.Item.Index, scrollPosition: ListViewScrollPosition, animated: Bool, filter: ChatListFilter?) case scroll(index: EngineChatList.Item.Index, sourceIndex: EngineChatList.Item.Index, scrollPosition: ListViewScrollPosition, animated: Bool, filter: ChatListFilter?)
var filter: ChatListFilter? { public var filter: ChatListFilter? {
switch self { switch self {
case let .initial(_, filter): case let .initial(_, filter):
return filter return filter
@ -23,10 +23,16 @@ enum ChatListNodeLocation: Equatable {
} }
} }
struct ChatListNodeViewUpdate { public struct ChatListNodeViewUpdate {
let list: EngineChatList public let list: EngineChatList
let type: ViewUpdateType public let type: ViewUpdateType
let scrollPosition: ChatListNodeViewScrollPosition? public let scrollPosition: ChatListNodeViewScrollPosition?
public init(list: EngineChatList, type: ViewUpdateType, scrollPosition: ChatListNodeViewScrollPosition?) {
self.list = list
self.type = type
self.scrollPosition = scrollPosition
}
} }
public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: EnginePeer.Id) -> ChatListFilterPredicate { public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: EnginePeer.Id) -> ChatListFilterPredicate {
@ -113,7 +119,7 @@ public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: E
}) })
} }
func chatListViewForLocation(chatListLocation: ChatListControllerLocation, location: ChatListNodeLocation, account: Account, shouldLoadCanMessagePeer: Bool) -> Signal<ChatListNodeViewUpdate, NoError> { public func chatListViewForLocation(chatListLocation: ChatListControllerLocation, location: ChatListNodeLocation, account: Account, shouldLoadCanMessagePeer: Bool) -> Signal<ChatListNodeViewUpdate, NoError> {
let accountPeerId = account.peerId let accountPeerId = account.peerId
switch chatListLocation { switch chatListLocation {

View File

@ -47,7 +47,7 @@ struct ChatListNodeViewTransition {
let animateCrossfade: Bool let animateCrossfade: Bool
} }
enum ChatListNodeViewScrollPosition { public enum ChatListNodeViewScrollPosition {
case index(index: ChatListIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool) case index(index: ChatListIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool)
} }

View File

@ -2376,16 +2376,24 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
} else { } else {
let updateItem = updateIndicesAndItems[0] let updateItem = updateIndicesAndItems[0]
if let previousNode = previousNodes[updateItem.index] { if let previousNode = previousNodes[updateItem.index] {
let threadId = pthread_self()
var tailRecurse = false
self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right, availableHeight: state.visibleSize.height - state.insets.top - state.insets.bottom), updateAnimationIsAnimated: animated, updateAnimationIsCrossfade: crossfade, customAnimationTransition: customAnimationTransition, completion: { _, layout, apply in self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right, availableHeight: state.visibleSize.height - state.insets.top - state.insets.bottom), updateAnimationIsAnimated: animated, updateAnimationIsCrossfade: crossfade, customAnimationTransition: customAnimationTransition, completion: { _, layout, apply in
state.updateNodeAtItemIndex(updateItem.index, layout: layout, direction: updateItem.directionHint, isAnimated: animated, apply: apply, operations: &operations) state.updateNodeAtItemIndex(updateItem.index, layout: layout, direction: updateItem.directionHint, isAnimated: animated, apply: apply, operations: &operations)
updateIndicesAndItems.remove(at: 0) updateIndicesAndItems.remove(at: 0)
if pthread_equal(pthread_self(), threadId) != 0 && !tailRecurse {
tailRecurse = true
} else {
self.updateNodes(synchronous: synchronous, synchronousLoads: synchronousLoads, crossfade: crossfade, customAnimationTransition: customAnimationTransition, animated: animated, updateIndicesAndItems: updateIndicesAndItems, inputState: state, previousNodes: previousNodes, inputOperations: operations, completion: completion) self.updateNodes(synchronous: synchronous, synchronousLoads: synchronousLoads, crossfade: crossfade, customAnimationTransition: customAnimationTransition, animated: animated, updateIndicesAndItems: updateIndicesAndItems, inputState: state, previousNodes: previousNodes, inputOperations: operations, completion: completion)
}
}) })
if !tailRecurse {
tailRecurse = true
break break
}
} else { } else {
updateIndicesAndItems.remove(at: 0) updateIndicesAndItems.remove(at: 0)
//self.updateNodes(synchronous: synchronous, animated: animated, updateIndicesAndItems: updateIndicesAndItems, inputState: state, previousNodes: previousNodes, inputOperations: operations, completion: completion)
} }
} }
} }

View File

@ -424,10 +424,10 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
} }
open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: 0.0, transition: transition) self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: 0.0, additionalCutout: nil, transition: transition)
} }
public func applyNavigationBarLayout(_ layout: ContainerViewLayout, navigationLayout: NavigationLayout, additionalBackgroundHeight: CGFloat, transition: ContainedViewLayoutTransition) { public func applyNavigationBarLayout(_ layout: ContainerViewLayout, navigationLayout: NavigationLayout, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, transition: ContainedViewLayoutTransition) {
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0 let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: navigationLayout.navigationFrame.maxY)) var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: navigationLayout.navigationFrame.maxY))

View File

@ -1,4 +1,5 @@
#import "RuntimeUtils.h" #import "RuntimeUtils.h"
#include <Foundation/Foundation.h>
#import <objc/runtime.h> #import <objc/runtime.h>

View File

@ -977,7 +977,7 @@ public class ReplaceBoostScreen: ViewController {
layout.statusBarHeight = nil layout.statusBarHeight = nil
self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, transition: transition) self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, additionalCutout: nil, transition: transition)
} }
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {

View File

@ -394,13 +394,14 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
private var suspendNavigationBarLayout: Bool = false private var suspendNavigationBarLayout: Bool = false
private var suspendedNavigationBarLayout: ContainerViewLayout? private var suspendedNavigationBarLayout: ContainerViewLayout?
private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0 private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
private var additionalNavigationBarCutout: CGSize?
override open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
if self.suspendNavigationBarLayout { if self.suspendNavigationBarLayout {
self.suspendedNavigationBarLayout = layout self.suspendedNavigationBarLayout = layout
return return
} }
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition) self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: self.additionalNavigationBarCutout, transition: transition)
} }
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
@ -969,7 +970,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
self.suspendNavigationBarLayout = false self.suspendNavigationBarLayout = false
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout { if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition) self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalNavigationBarCutout: self.additionalNavigationBarCutout, transition: transition)
} }
self.accessoryPanelContainerHeight = additionalHeight self.accessoryPanelContainerHeight = additionalHeight

View File

@ -71,6 +71,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
case empty case empty
case tag(MemoryBuffer) case tag(MemoryBuffer)
case search(query: String, includeSavedPeers: Bool) case search(query: String, includeSavedPeers: Bool)
case monoforumChats(query: String)
} }
public let context: AccountContext public let context: AccountContext
@ -85,6 +86,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
public let loadTagMessages: (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>? public let loadTagMessages: (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>?
public let getSearchResult: () -> Signal<SearchMessagesResult?, NoError>? public let getSearchResult: () -> Signal<SearchMessagesResult?, NoError>?
public let getSavedPeers: (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>? public let getSavedPeers: (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>?
public let getChats: (String) -> Signal<EngineChatList?, NoError>?
public let loadMoreSearchResults: () -> Void public let loadMoreSearchResults: () -> Void
public init( public init(
@ -100,6 +102,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
loadTagMessages: @escaping (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>?, loadTagMessages: @escaping (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>?,
getSearchResult: @escaping () -> Signal<SearchMessagesResult?, NoError>?, getSearchResult: @escaping () -> Signal<SearchMessagesResult?, NoError>?,
getSavedPeers: @escaping (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>?, getSavedPeers: @escaping (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>?,
getChats: @escaping (String) -> Signal<EngineChatList?, NoError>?,
loadMoreSearchResults: @escaping () -> Void loadMoreSearchResults: @escaping () -> Void
) { ) {
self.context = context self.context = context
@ -114,6 +117,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
self.loadTagMessages = loadTagMessages self.loadTagMessages = loadTagMessages
self.getSearchResult = getSearchResult self.getSearchResult = getSearchResult
self.getSavedPeers = getSavedPeers self.getSavedPeers = getSavedPeers
self.getChats = getChats
self.loadMoreSearchResults = loadMoreSearchResults self.loadMoreSearchResults = loadMoreSearchResults
} }
@ -146,10 +150,12 @@ public final class ChatInlineSearchResultsListComponent: Component {
enum Id: Hashable { enum Id: Hashable {
case peer(EnginePeer.Id) case peer(EnginePeer.Id)
case message(EngineMessage.Id) case message(EngineMessage.Id)
case chat(EngineChatList.Item.Id)
} }
case peer(EnginePeer) case peer(EnginePeer)
case message(EngineMessage) case message(EngineMessage)
case chat(EngineChatList.Item)
var id: Id { var id: Id {
switch self { switch self {
@ -157,6 +163,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
return .peer(peer.id) return .peer(peer.id)
case let .message(message): case let .message(message):
return .message(message.id) return .message(message.id)
case let .chat(chat):
return .chat(chat.id)
} }
} }
@ -174,6 +182,12 @@ public final class ChatInlineSearchResultsListComponent: Component {
} else { } else {
return false return false
} }
case let .chat(chat):
if case .chat(chat) = rhs {
return true
} else {
return false
}
} }
} }
@ -188,14 +202,27 @@ public final class ChatInlineSearchResultsListComponent: Component {
return lhsPeer.id < rhsPeer.id return lhsPeer.id < rhsPeer.id
case .message: case .message:
return true return true
case .chat:
return true
} }
case let .message(lhsMessage): case let .message(lhsMessage):
switch rhs { switch rhs {
case .peer: case .peer:
return false return false
case .chat:
return false
case let .message(rhsMessage): case let .message(rhsMessage):
return lhsMessage.index > rhsMessage.index return lhsMessage.index > rhsMessage.index
} }
case let .chat(lhsChat):
switch rhs {
case let .chat(rhsChat):
return lhsChat.index > rhsChat.index
case .peer:
return false
case .message:
return true
}
} }
} }
} }
@ -418,6 +445,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
} }
case .search: case .search:
break break
case .monoforumChats:
break
} }
} }
} else if let (currentIndex, disposable) = self.searchContents { } else if let (currentIndex, disposable) = self.searchContents {
@ -431,6 +460,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
self.searchContents = (loadAroundIndex, disposable) self.searchContents = (loadAroundIndex, disposable)
component.loadMoreSearchResults() component.loadMoreSearchResults()
case .monoforumChats:
break
} }
} }
} }
@ -581,6 +612,127 @@ public final class ChatInlineSearchResultsListComponent: Component {
})) }))
} }
} }
case let .monoforumChats(query):
let _ = query
if previousComponent?.contents != component.contents {
self.tagContents?.disposable?.dispose()
self.tagContents = nil
self.searchContents?.disposable?.dispose()
self.searchContents = nil
let disposable = MetaDisposable()
self.searchContents = (nil, disposable)
let savedPeers: Signal<EngineChatList?, NoError>
if let savedPeersSignal = component.getChats(query) {
savedPeers = savedPeersSignal
} else {
savedPeers = .single(nil)
}
disposable.set((savedPeers
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
guard let self else {
return
}
let messages: [EngineMessage] = [] /*result?.messages.map { entry in
return EngineMessage(entry)
} ?? []*/
var entries: [Entry] = []
if let chatList {
for item in chatList.items {
entries.append(.chat(item))
}
}
for message in messages {
entries.append(.message(message))
}
entries.sort()
let contentsId = self.nextContentsId
self.nextContentsId += 1
self.contentsState = ContentsState(
id: contentsId,
contentId: .search(query),
entries: entries,
messages: messages,
hasEarlier: false, //!(result?.completed ?? true),
hasLater: false
)
if !self.isUpdating {
self.state?.updated(transition: .immediate)
}
if !self.didSetReady {
self.didSetReady = true
self.isReadyPromise.set(.single(true))
}
}))
/*if !query.isEmpty, let savedPeersSignal = component.getSavedPeers(query) {
savedPeers = savedPeersSignal
} else {
savedPeers = .single([])
}*/
/*if let historySignal = component.getSearchResult() {
disposable.set((savedPeers
|> mapToSignal { savedPeers -> Signal<([(EnginePeer, MessageIndex?)], SearchMessagesResult?), NoError> in
if savedPeers.isEmpty {
return historySignal
|> map { result in
return ([], result)
}
} else {
return (.single(nil) |> then(historySignal))
|> map { result in
return (savedPeers, result)
}
}
}
|> deliverOnMainQueue).startStrict(next: { [weak self] savedPeers, result in
guard let self else {
return
}
let messages: [EngineMessage] = result?.messages.map { entry in
return EngineMessage(entry)
} ?? []
var entries: [Entry] = []
for (peer, _) in savedPeers {
entries.append(.peer(peer))
}
for message in messages {
entries.append(.message(message))
}
entries.sort()
let contentsId = self.nextContentsId
self.nextContentsId += 1
self.contentsState = ContentsState(
id: contentsId,
contentId: .search(query),
entries: entries,
messages: messages,
hasEarlier: !(result?.completed ?? true),
hasLater: false
)
if !self.isUpdating {
self.state?.updated(transition: .immediate)
}
if !self.didSetReady {
self.didSetReady = true
self.isReadyPromise.set(.single(true))
}
}))
}*/
}
} }
if let contentsState = self.contentsState, self.contentsState != self.appliedContentsState { if let contentsState = self.contentsState, self.contentsState != self.appliedContentsState {
@ -607,13 +759,17 @@ public final class ChatInlineSearchResultsListComponent: Component {
}, },
additionalCategorySelected: { _ in additionalCategorySelected: { _ in
}, },
messageSelected: { [weak self] _, _, message, _ in messageSelected: { [weak self] peer, _, message, _ in
guard let self else { guard let self, let component = self.component else {
return return
} }
self.listNode.clearHighlightAnimated(true) self.listNode.clearHighlightAnimated(true)
self.component?.messageSelected(message) if case .monoforumChats = component.contents {
component.peerSelected(peer)
} else {
component.messageSelected(message)
}
}, },
groupSelected: { _ in groupSelected: { _ in
}, },
@ -845,6 +1001,45 @@ public final class ChatInlineSearchResultsListComponent: Component {
hiddenOffset: false, hiddenOffset: false,
interaction: chatListNodeInteraction interaction: chatListNodeInteraction
) )
case let .chat(item):
return ChatListItem(
presentationData: chatListPresentationData,
context: component.context,
chatListLocation: component.peerId.flatMap { peerId in .savedMessagesChats(peerId: peerId) } ?? .chatList(groupId: .root),
filterData: nil,
index: item.index,
content: .peer(ChatListItemContent.PeerData(
messages: item.messages,
peer: item.renderedPeer,
threadInfo: nil,
combinedReadState: nil,
isRemovedFromTotalUnreadCount: false,
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
draftState: nil,
mediaDraftContentType: nil,
inputActivities: nil,
promoInfo: nil,
ignoreUnreadBadge: false,
displayAsMessage: component.peerId != component.context.account.peerId && !component.showEmptyResults,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: [],
autoremoveTimeout: nil,
storyState: nil,
requiresPremiumForMessaging: false,
displayAsTopicList: false,
tags: []
)),
editing: false,
hasActiveRevealControls: false,
selected: false,
header: nil,
enableContextActions: false,
hiddenOffset: false,
interaction: chatListNodeInteraction
)
} }
} }

View File

@ -37,6 +37,7 @@ public final class ChatSideTopicsPanel: Component {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let peerId: EnginePeer.Id let peerId: EnginePeer.Id
let isMonoforum: Bool
let topicId: Int64? let topicId: Int64?
let togglePanel: () -> Void let togglePanel: () -> Void
let updateTopicId: (Int64?) -> Void let updateTopicId: (Int64?) -> Void
@ -46,6 +47,7 @@ public final class ChatSideTopicsPanel: Component {
theme: PresentationTheme, theme: PresentationTheme,
strings: PresentationStrings, strings: PresentationStrings,
peerId: EnginePeer.Id, peerId: EnginePeer.Id,
isMonoforum: Bool,
topicId: Int64?, topicId: Int64?,
togglePanel: @escaping () -> Void, togglePanel: @escaping () -> Void,
updateTopicId: @escaping (Int64?) -> Void updateTopicId: @escaping (Int64?) -> Void
@ -54,6 +56,7 @@ public final class ChatSideTopicsPanel: Component {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.peerId = peerId self.peerId = peerId
self.isMonoforum = isMonoforum
self.topicId = topicId self.topicId = topicId
self.togglePanel = togglePanel self.togglePanel = togglePanel
self.updateTopicId = updateTopicId self.updateTopicId = updateTopicId
@ -72,6 +75,9 @@ public final class ChatSideTopicsPanel: Component {
if lhs.peerId != rhs.peerId { if lhs.peerId != rhs.peerId {
return false return false
} }
if lhs.isMonoforum != rhs.isMonoforum {
return false
}
if lhs.topicId != rhs.topicId { if lhs.topicId != rhs.topicId {
return false return false
} }
@ -111,7 +117,7 @@ public final class ChatSideTopicsPanel: Component {
private let containerButton: HighlightTrackingButton private let containerButton: HighlightTrackingButton
private let icon = ComponentView<Empty>() private var icon: ComponentView<Empty>?
private var avatarNode: AvatarNode? private var avatarNode: AvatarNode?
private let title = ComponentView<Empty>() private let title = ComponentView<Empty>()
@ -179,18 +185,22 @@ public final class ChatSideTopicsPanel: Component {
func update(context: AccountContext, item: Item, isSelected: Bool, theme: PresentationTheme, width: CGFloat, transition: ComponentTransition) -> CGSize { func update(context: AccountContext, item: Item, isSelected: Bool, theme: PresentationTheme, width: CGFloat, transition: ComponentTransition) -> CGSize {
let spacing: CGFloat = 3.0 let spacing: CGFloat = 3.0
let iconSize = CGSize(width: 30.0, height: 30.0)
let avatarIconContent: EmojiStatusComponent.Content var avatarIconContent: EmojiStatusComponent.Content?
if case let .forum(topicId) = item.item.id, topicId != 1, let threadData = item.item.threadData { if case let .forum(topicId) = item.item.id {
if topicId != 1, let threadData = item.item.threadData {
if let fileId = threadData.info.icon, fileId != 0 { if let fileId = threadData.info.icon, fileId != 0 {
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0)) avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: iconSize, placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
} else { } else {
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: CGSize(width: 18.0, height: 18.0)) avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize)
} }
} else { } else {
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme)) avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme))
} }
}
if let avatarIconContent {
let avatarIconComponent = EmojiStatusComponent( let avatarIconComponent = EmojiStatusComponent(
context: context, context: context,
animationCache: context.animationCache, animationCache: context.animationCache,
@ -199,14 +209,54 @@ public final class ChatSideTopicsPanel: Component {
isVisibleForAnimations: false, isVisibleForAnimations: false,
action: nil action: nil
) )
let iconSize = self.icon.update( let icon: ComponentView<Empty>
if let current = self.icon {
icon = current
} else {
icon = ComponentView()
self.icon = icon
}
let _ = icon.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(avatarIconComponent), component: AnyComponent(avatarIconComponent),
environment: {}, environment: {},
containerSize: CGSize(width: 30.0, height: 30.0) containerSize: iconSize
) )
} else if let icon = self.icon {
self.icon = nil
icon.view?.removeFromSuperview()
}
let titleText: String
if case let .forum(topicId) = item.item.id {
let _ = topicId
if let threadData = item.item.threadData {
titleText = threadData.info.title
} else {
//TODO:localize
titleText = "General"
}
} else {
titleText = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
}
if let avatarIconContent, let icon = self.icon {
let avatarIconComponent = EmojiStatusComponent(
context: context,
animationCache: context.animationCache,
animationRenderer: context.animationRenderer,
content: avatarIconContent,
isVisibleForAnimations: false,
action: nil
)
let _ = icon.update(
transition: .immediate,
component: AnyComponent(avatarIconComponent),
environment: {},
containerSize: iconSize
)
}
let titleText: String = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
let titleSize = self.title.update( let titleSize = self.title.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(MultilineTextComponent( component: AnyComponent(MultilineTextComponent(
@ -224,16 +274,20 @@ public final class ChatSideTopicsPanel: Component {
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) * 0.5), y: 0.0), size: iconSize) let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) * 0.5), y: 0.0), size: iconSize)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: iconFrame.maxY + spacing), size: titleSize) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: iconFrame.maxY + spacing), size: titleSize)
if let iconView = self.icon.view { if let icon = self.icon {
if let avatarNode = self.avatarNode {
self.avatarNode = nil
avatarNode.view.removeFromSuperview()
}
if let iconView = icon.view {
if iconView.superview == nil { if iconView.superview == nil {
iconView.isUserInteractionEnabled = false iconView.isUserInteractionEnabled = false
self.containerButton.addSubview(iconView) self.containerButton.addSubview(iconView)
} }
iconView.frame = iconFrame iconView.frame = iconFrame
}
if "".isEmpty { } else {
iconView.isHidden = true
let avatarNode: AvatarNode let avatarNode: AvatarNode
if let current = self.avatarNode { if let current = self.avatarNode {
avatarNode = current avatarNode = current
@ -253,11 +307,6 @@ public final class ChatSideTopicsPanel: Component {
avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size) avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
} }
} }
} else if let avatarNode = self.avatarNode {
self.avatarNode = nil
avatarNode.view.removeFromSuperview()
iconView.isHidden = false
}
} }
if let titleView = self.title.view { if let titleView = self.title.view {
@ -600,7 +649,15 @@ public final class ChatSideTopicsPanel: Component {
public func topicIndex(threadId: Int64?) -> Int? { public func topicIndex(threadId: Int64?) -> Int? {
if let threadId { if let threadId {
if let value = self.items.firstIndex(where: { $0.id == .chatList(PeerId(threadId)) }) { if let value = self.items.firstIndex(where: { item in
if item.id == .chatList(PeerId(threadId)) {
return true
} else if item.id == .forum(threadId) {
return true
} else {
return false
}
}) {
return value + 1 return value + 1
} else { } else {
return nil return nil
@ -619,77 +676,7 @@ public final class ChatSideTopicsPanel: Component {
self.state = state self.state = state
if self.component == nil { if self.component == nil {
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: component.peerId) let threadListSignal: Signal<EngineChatList, NoError> = component.context.sharedContext.subscribeChatListData(context: component.context, location: component.isMonoforum ? .savedMessagesChats(peerId: component.peerId) : .forum(peerId: component.peerId))
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: component.peerId)
let accountPeerId = component.context.account.peerId
let threadListSignal: Signal<EngineChatList, NoError> = component.context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey])
|> map { views -> EngineChatList in
guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else {
preconditionFailure()
}
var draft: EngineChatList.Draft?
if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView {
if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp {
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
if let text = opaqueState.synchronizeableInputState?.text {
draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? [])
}
}
}
}
var items: [EngineChatList.Item] = []
for item in view.items {
guard let sourcePeer = item.peer else {
continue
}
let sourceId = PeerId(item.id)
var messages: [EngineMessage] = []
if let topMessage = item.topMessage {
messages.append(EngineMessage(topMessage))
}
let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp)
items.append(EngineChatList.Item(
id: .chatList(sourceId),
index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)),
messages: messages,
readCounters: nil,
isMuted: false,
draft: sourceId == accountPeerId ? draft : nil,
threadData: nil,
renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)),
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
forumTopicData: nil,
topForumTopicItems: [],
hasFailed: false,
isContact: false,
autoremoveTimeout: nil,
storyStats: nil,
displayAsTopicList: false,
isPremiumRequiredToMessage: false,
mediaDraftContentType: nil
))
}
let list = EngineChatList(
items: items.reversed(),
groupItems: [],
additionalItems: [],
hasEarlier: false,
hasLater: false,
isLoading: view.isLoading
)
return list
}
self.itemsDisposable = (threadListSignal self.itemsDisposable = (threadListSignal
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in |> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
@ -864,8 +851,12 @@ public final class ChatSideTopicsPanel: Component {
guard let self, let component = self.component else { guard let self, let component = self.component else {
return return
} }
if case let .forum(topicId) = item.item.id {
component.updateTopicId(topicId)
} else {
let topicId = chatListItem.renderedPeer.peerId.toInt64() let topicId = chatListItem.renderedPeer.peerId.toInt64()
component.updateTopicId(topicId) component.updateTopicId(topicId)
}
}, contextGesture: { gesture, sourceNode in }, contextGesture: { gesture, sourceNode in
}) })
self.itemViews[itemId] = itemView self.itemViews[itemId] = itemView
@ -873,8 +864,10 @@ public final class ChatSideTopicsPanel: Component {
} }
var isSelected = false var isSelected = false
if component.topicId == item.item.renderedPeer.peerId.toInt64() { if case let .forum(topicId) = item.item.id {
isSelected = true isSelected = component.topicId == topicId
} else {
isSelected = component.topicId == item.item.renderedPeer.peerId.toInt64()
} }
let itemSize = itemView.update(context: component.context, item: item, isSelected: isSelected, theme: component.theme, width: panelWidth, transition: .immediate) let itemSize = itemView.update(context: component.context, item: item, isSelected: isSelected, theme: component.theme, width: panelWidth, transition: .immediate)
let itemFrame = CGRect(origin: CGPoint(x: containerInsets.left, y: contentSize.height), size: itemSize) let itemFrame = CGRect(origin: CGPoint(x: containerInsets.left, y: contentSize.height), size: itemSize)

View File

@ -2729,9 +2729,10 @@ extension ChatControllerImpl {
} }
mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: false), updateSizeAndInsets) mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: false), updateSizeAndInsets)
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, _ in }, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, cutout, _ in
strongSelf.additionalNavigationBarBackgroundHeight = value strongSelf.additionalNavigationBarBackgroundHeight = value
strongSelf.additionalNavigationBarHitTestSlop = hitTestSlop strongSelf.additionalNavigationBarHitTestSlop = hitTestSlop
strongSelf.additionalNavigationBarCutout = cutout
}) })
if let mappedTransition = mappedTransition { if let mappedTransition = mappedTransition {

View File

@ -7072,13 +7072,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var suspendedNavigationBarLayout: ContainerViewLayout? var suspendedNavigationBarLayout: ContainerViewLayout?
var additionalNavigationBarBackgroundHeight: CGFloat = 0.0 var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
var additionalNavigationBarHitTestSlop: CGFloat = 0.0 var additionalNavigationBarHitTestSlop: CGFloat = 0.0
var additionalNavigationBarCutout: CGSize?
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
if self.suspendNavigationBarLayout { if self.suspendNavigationBarLayout {
self.suspendedNavigationBarLayout = layout self.suspendedNavigationBarLayout = layout
return return
} }
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition) self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: self.additionalNavigationBarCutout, transition: transition)
} }
override public func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? { override public func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
@ -7116,10 +7117,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var navigationBarTransition = transition var navigationBarTransition = transition
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion) self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, extraNavigationTransition in }, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, cutout, extraNavigationTransition in
navigationBarTransition = extraNavigationTransition navigationBarTransition = extraNavigationTransition
self.additionalNavigationBarBackgroundHeight = value self.additionalNavigationBarBackgroundHeight = value
self.additionalNavigationBarHitTestSlop = hitTestSlop self.additionalNavigationBarHitTestSlop = hitTestSlop
self.additionalNavigationBarCutout = cutout
}) })
if case .compact = layout.metrics.widthClass { if case .compact = layout.metrics.widthClass {
@ -7138,7 +7140,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.suspendNavigationBarLayout = false self.suspendNavigationBarLayout = false
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout { if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: navigationBarTransition) self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: self.additionalNavigationBarCutout, transition: navigationBarTransition)
} }
self.navigationBar?.additionalContentNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.additionalNavigationBarHitTestSlop, right: 0.0) self.navigationBar?.additionalContentNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.additionalNavigationBarHitTestSlop, right: 0.0)
} }

View File

@ -351,7 +351,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if let (layout, navigationHeight) = self.validLayout { if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in
}, updateExtraNavigationBarBackgroundHeight: { _, _, _ in }, updateExtraNavigationBarBackgroundHeight: { _, _, _, _ in
}) })
} }
} }
@ -1033,7 +1033,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.wrappingNode.update(size: layout.size, cornerRadius: layout.deviceMetrics.screenCornerRadius, transition: .immediate) self.wrappingNode.update(size: layout.size, cornerRadius: layout.deviceMetrics.screenCornerRadius, transition: .immediate)
} }
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, CGFloat, ContainedViewLayoutTransition) -> Void) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, CGFloat, CGSize?, ContainedViewLayoutTransition) -> Void) {
let transition: ContainedViewLayoutTransition let transition: ContainedViewLayoutTransition
if let _ = self.scheduledAnimateInAsOverlayFromNode { if let _ = self.scheduledAnimateInAsOverlayFromNode {
transition = .immediate transition = .immediate
@ -1302,6 +1302,28 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.titleTopicsAccessoryPanelNode = nil self.titleTopicsAccessoryPanelNode = nil
} }
var defaultLeftPanelWidth: CGFloat = 72.0
defaultLeftPanelWidth += layout.safeInsets.left
let leftPanelLeftInset = defaultLeftPanelWidth - 72.0
var leftPanelSize: CGSize?
var dismissedLeftPanel: (component: AnyComponentWithIdentity<ChatSidePanelEnvironment>, view: ComponentView<ChatSidePanelEnvironment>)?
var immediatelyLayoutLeftPanelNodeAndAnimateAppearance = false
if let leftPanelComponent = sidePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.leftPanel?.component, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction, force: false) {
if self.leftPanel?.component.id != leftPanelComponent.id {
dismissedLeftPanel = self.leftPanel
self.leftPanel = (leftPanelComponent, ComponentView())
immediatelyLayoutLeftPanelNodeAndAnimateAppearance = true
} else if let leftPanel = self.leftPanel {
self.leftPanel = (leftPanelComponent, leftPanel.view)
}
leftPanelSize = CGSize(width: defaultLeftPanelWidth, height: layout.size.height)
} else if let leftPanel = self.leftPanel {
dismissedLeftPanel = leftPanel
self.leftPanel = nil
}
var dismissedTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode? var dismissedTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode?
var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false
var titleAccessoryPanelHeight: CGFloat? var titleAccessoryPanelHeight: CGFloat?
@ -1321,7 +1343,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
} }
let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState) let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: leftPanelSize?.width ?? layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
titleAccessoryPanelHeight = layoutResult.insetHeight titleAccessoryPanelHeight = layoutResult.insetHeight
titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight
titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop
@ -1489,30 +1511,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.feePanelNode = nil self.feePanelNode = nil
} }
var defaultLeftPanelWidth: CGFloat = 72.0
if case .landscapeRight = layout.metrics.orientation {
defaultLeftPanelWidth += layout.safeInsets.left
}
let leftPanelLeftInset = defaultLeftPanelWidth - 72.0
var leftPanelSize: CGSize?
var dismissedLeftPanel: (component: AnyComponentWithIdentity<ChatSidePanelEnvironment>, view: ComponentView<ChatSidePanelEnvironment>)?
var immediatelyLayoutLeftPanelNodeAndAnimateAppearance = false
if let leftPanelComponent = sidePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.leftPanel?.component, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction, force: false) {
if self.leftPanel?.component.id != leftPanelComponent.id {
dismissedLeftPanel = self.leftPanel
self.leftPanel = (leftPanelComponent, ComponentView())
immediatelyLayoutLeftPanelNodeAndAnimateAppearance = true
} else if let leftPanel = self.leftPanel {
self.leftPanel = (leftPanelComponent, leftPanel.view)
}
leftPanelSize = CGSize(width: defaultLeftPanelWidth, height: layout.size.height)
} else if let leftPanel = self.leftPanel {
dismissedLeftPanel = leftPanel
self.leftPanel = nil
}
self.controllerInteraction.isSidePanelOpen = self.leftPanel != nil self.controllerInteraction.isSidePanelOpen = self.leftPanel != nil
var inputPanelNodeBaseHeight: CGFloat = 0.0 var inputPanelNodeBaseHeight: CGFloat = 0.0
@ -1793,6 +1791,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.historyNode.isSelectionGestureEnabled = isSelectionEnabled self.historyNode.isSelectionGestureEnabled = isSelectionEnabled
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 200.0))) transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 200.0)))
self.titleAccessoryPanelContainer.hitTestExcludeInsets = UIEdgeInsets(top: 0.0, left: leftPanelSize?.width ?? 0.0, bottom: 0.0, right: 0.0)
transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
@ -1849,7 +1848,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
extraNavigationBarHeight += panelHeight extraNavigationBarHeight += panelHeight
} }
updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraTransition) var extraNavigationBarLeftCutout: CGSize?
extraNavigationBarLeftCutout = CGSize(width: 44.0, height: 30.0)
updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraNavigationBarLeftCutout, extraTransition)
let contentBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height - wrappingInsets.top - wrappingInsets.bottom) let contentBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height - wrappingInsets.top - wrappingInsets.bottom)
@ -2270,10 +2272,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
if immediatelyLayoutLeftPanelNodeAndAnimateAppearance { if immediatelyLayoutLeftPanelNodeAndAnimateAppearance {
leftPanelView.frame = leftPanelFrame.offsetBy(dx: -leftPanelSize.width, dy: 0.0) leftPanelView.frame = leftPanelFrame.offsetBy(dx: -leftPanelSize.width, dy: 0.0)
if self.titleTopicsAccessoryPanelNode != nil {
if let leftPanelView = leftPanelView as? ChatSideTopicsPanel.View { if let leftPanelView = leftPanelView as? ChatSideTopicsPanel.View {
leftPanelView.updateGlobalOffset(globalOffset: -leftPanelSize.width, transition: ComponentTransition(transition)) leftPanelView.updateGlobalOffset(globalOffset: -leftPanelSize.width, transition: ComponentTransition(transition))
} }
} }
}
transition.updateFrame(view: leftPanelView, frame: leftPanelFrame) transition.updateFrame(view: leftPanelView, frame: leftPanelFrame)
if let leftPanelView = leftPanelView as? ChatSideTopicsPanel.View { if let leftPanelView = leftPanelView as? ChatSideTopicsPanel.View {
leftPanelView.updateGlobalOffset(globalOffset: 0.0, transition: ComponentTransition(transition)) leftPanelView.updateGlobalOffset(globalOffset: 0.0, transition: ComponentTransition(transition))
@ -2298,9 +2303,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
dismissedLeftPanelView?.removeFromSuperview() dismissedLeftPanelView?.removeFromSuperview()
}) })
if let dismissedLeftPanelView = dismissedLeftPanelView as? ChatSideTopicsPanel.View { if let dismissedLeftPanelView = dismissedLeftPanelView as? ChatSideTopicsPanel.View {
if self.titleTopicsAccessoryPanelNode != nil {
dismissedLeftPanelView.updateGlobalOffset(globalOffset: -dismissedLeftPanelSize.width, transition: ComponentTransition(transition)) dismissedLeftPanelView.updateGlobalOffset(globalOffset: -dismissedLeftPanelSize.width, transition: ComponentTransition(transition))
} }
} }
}
if let navigationBarBackgroundContent = self.navigationBarBackgroundContent { if let navigationBarBackgroundContent = self.navigationBarBackgroundContent {
transition.updateFrame(node: navigationBarBackgroundContent, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight + (titleAccessoryPanelBackgroundHeight ?? 0.0) + (translationPanelHeight ?? 0.0))), beginWithCurrentState: true) transition.updateFrame(node: navigationBarBackgroundContent, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight + (titleAccessoryPanelBackgroundHeight ?? 0.0) + (translationPanelHeight ?? 0.0))), beginWithCurrentState: true)
@ -2352,7 +2359,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if let titleTopicsAccessoryPanelNode = self.titleTopicsAccessoryPanelNode, let titleTopicsAccessoryPanelFrame, (immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance || !titleTopicsAccessoryPanelNode.frame.equalTo(titleTopicsAccessoryPanelFrame)) { if let titleTopicsAccessoryPanelNode = self.titleTopicsAccessoryPanelNode, let titleTopicsAccessoryPanelFrame, (immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance || !titleTopicsAccessoryPanelNode.frame.equalTo(titleTopicsAccessoryPanelFrame)) {
if immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance { if immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance {
titleTopicsAccessoryPanelNode.frame = titleTopicsAccessoryPanelFrame.offsetBy(dx: 0.0, dy: -titleTopicsAccessoryPanelFrame.height) titleTopicsAccessoryPanelNode.frame = titleTopicsAccessoryPanelFrame.offsetBy(dx: 0.0, dy: -titleTopicsAccessoryPanelFrame.height)
if self.leftPanel != nil {
titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -titleTopicsAccessoryPanelFrame.height, transition: .immediate) titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -titleTopicsAccessoryPanelFrame.height, transition: .immediate)
}
let topPanelTransition = ComponentTransition(transition) let topPanelTransition = ComponentTransition(transition)
/*switch topPanelTransition.animation { /*switch topPanelTransition.animation {
@ -2378,8 +2387,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
let previousFrame = titleAccessoryPanelNode.frame let previousFrame = titleAccessoryPanelNode.frame
titleAccessoryPanelNode.frame = titleAccessoryPanelFrame titleAccessoryPanelNode.frame = titleAccessoryPanelFrame
if transition.isAnimated && previousFrame.width != titleAccessoryPanelFrame.width { if transition.isAnimated && previousFrame.width != titleAccessoryPanelFrame.width {
} else { } else if immediatelyLayoutAccessoryPanelAndAnimateAppearance {
transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height)) transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height))
} else if previousFrame.minY != titleAccessoryPanelFrame.minY {
transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: previousFrame.minY - titleAccessoryPanelFrame.minY))
} }
} }
@ -2502,8 +2513,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
transition.updateFrame(node: dismissedTitleTopicsAccessoryPanelNode, frame: dismissedTopPanelFrame, completion: { [weak dismissedTitleTopicsAccessoryPanelNode] _ in transition.updateFrame(node: dismissedTitleTopicsAccessoryPanelNode, frame: dismissedTopPanelFrame, completion: { [weak dismissedTitleTopicsAccessoryPanelNode] _ in
dismissedTitleTopicsAccessoryPanelNode?.removeFromSupernode() dismissedTitleTopicsAccessoryPanelNode?.removeFromSupernode()
}) })
if self.leftPanel != nil {
dismissedTitleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -dismissedTopPanelFrame.height, transition: ComponentTransition(transition)) dismissedTitleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -dismissedTopPanelFrame.height, transition: ComponentTransition(transition))
} }
}
if let dismissedTitleAccessoryPanelNode { if let dismissedTitleAccessoryPanelNode {
var dismissedPanelFrame = dismissedTitleAccessoryPanelNode.frame var dismissedPanelFrame = dismissedTitleAccessoryPanelNode.frame
@ -2802,6 +2815,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
displayInlineSearch = true displayInlineSearch = true
} }
} }
if let channel = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = self.chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
if self.chatPresentationInterfaceState.search != nil {
displayInlineSearch = true
}
}
if displayInlineSearch { if displayInlineSearch {
let peerId = self.chatPresentationInterfaceState.chatLocation.peerId let peerId = self.chatPresentationInterfaceState.chatLocation.peerId
@ -2827,6 +2845,8 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} else { } else {
mappedContents = .empty mappedContents = .empty
} }
} else if let channel = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = self.chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
mappedContents = .monoforumChats(query: self.chatPresentationInterfaceState.search?.query ?? "")
} else if case .peer(self.context.account.peerId) = self.chatPresentationInterfaceState.chatLocation { } else if case .peer(self.context.account.peerId) = self.chatPresentationInterfaceState.chatLocation {
mappedContents = .tag(MemoryBuffer()) mappedContents = .tag(MemoryBuffer())
} else { } else {
@ -2952,6 +2972,14 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
guard let self else { guard let self else {
return return
} }
if let channel = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum {
self.interfaceInteraction?.updateChatLocationThread(peer.id.toInt64())
self.controller?.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in
return current.updatedSearch(nil)
})
} else {
guard let navigationController = self.controller?.navigationController as? NavigationController else { guard let navigationController = self.controller?.navigationController as? NavigationController else {
return return
} }
@ -2976,6 +3004,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
subject: nil, subject: nil,
keepStack: .always keepStack: .always
)) ))
}
}, },
loadTagMessages: { tag, index in loadTagMessages: { tag, index in
let input: ChatHistoryLocationInput let input: ChatHistoryLocationInput
@ -3048,6 +3077,89 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
return foundLocalPeers return foundLocalPeers
}, },
getChats: { [weak self] query in
guard let self else {
return nil
}
guard let peerId = self.chatPresentationInterfaceState.chatLocation.peerId else {
return nil
}
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId)
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: peerId)
let accountPeerId = self.context.account.peerId
let threadListSignal: Signal<EngineChatList?, NoError> = self.context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey])
|> map { views -> EngineChatList? in
guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else {
preconditionFailure()
}
var draft: EngineChatList.Draft?
if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView {
if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp {
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
if let text = opaqueState.synchronizeableInputState?.text {
draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? [])
}
}
}
}
var items: [EngineChatList.Item] = []
for item in view.items {
guard let sourcePeer = item.peer else {
continue
}
let sourceId = PeerId(item.id)
var messages: [EngineMessage] = []
if let topMessage = item.topMessage {
messages.append(EngineMessage(topMessage))
}
let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp)
items.append(EngineChatList.Item(
id: .chatList(sourceId),
index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)),
messages: messages,
readCounters: EnginePeerReadCounters(
incomingReadId: 0, outgoingReadId: 0, count: Int32(item.unreadCount), markedUnread: false),
isMuted: false,
draft: sourceId == accountPeerId ? draft : nil,
threadData: nil,
renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)),
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
forumTopicData: nil,
topForumTopicItems: [],
hasFailed: false,
isContact: false,
autoremoveTimeout: nil,
storyStats: nil,
displayAsTopicList: false,
isPremiumRequiredToMessage: false,
mediaDraftContentType: nil
))
}
let list = EngineChatList(
items: items.reversed(),
groupItems: [],
additionalItems: [],
hasEarlier: false,
hasLater: false,
isLoading: view.isLoading
)
return list
}
return threadListSignal
},
loadMoreSearchResults: { [weak self] in loadMoreSearchResults: { [weak self] in
guard let self, let controller = self.controller else { guard let self, let controller = self.controller else {
return return
@ -4052,7 +4164,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if let (layout, navigationHeight) = self.validLayout { if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion) self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
}, updateExtraNavigationBarBackgroundHeight: { _, _, _ in }, updateExtraNavigationBarBackgroundHeight: { _, _, _, _ in
}) })
} }
} }

View File

@ -3,7 +3,13 @@ import UIKit
import AsyncDisplayKit import AsyncDisplayKit
final class ChatControllerTitlePanelNodeContainer: ASDisplayNode { final class ChatControllerTitlePanelNodeContainer: ASDisplayNode {
var hitTestExcludeInsets = UIEdgeInsets()
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if point.x < self.hitTestExcludeInsets.left {
return nil
}
for subview in self.view.subviews { for subview in self.view.subviews {
if let result = subview.hitTest(self.view.convert(point, to: subview), with: event) { if let result = subview.hitTest(self.view.convert(point, to: subview), with: event) {
return result return result

View File

@ -232,13 +232,24 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
} }
func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTopicListTitleAccessoryPanelNode? { func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTopicListTitleAccessoryPanelNode? {
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil { if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil, chatPresentationInterfaceState.search == nil {
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId { if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId {
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode { if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
return currentPanel return currentPanel
} else { } else {
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId) let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: true)
panel.interfaceInteraction = interfaceInteraction
return panel
}
}
} else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, chatPresentationInterfaceState.search == nil {
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId {
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
return currentPanel
} else {
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: false)
panel.interfaceInteraction = interfaceInteraction panel.interfaceInteraction = interfaceInteraction
return panel return panel
} }
@ -253,7 +264,7 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState
return nil return nil
} }
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil { if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil, chatPresentationInterfaceState.search == nil {
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
if case .side = topicListDisplayMode { if case .side = topicListDisplayMode {
return AnyComponentWithIdentity( return AnyComponentWithIdentity(
@ -263,6 +274,28 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState
theme: chatPresentationInterfaceState.theme, theme: chatPresentationInterfaceState.theme,
strings: chatPresentationInterfaceState.strings, strings: chatPresentationInterfaceState.strings,
peerId: peerId, peerId: peerId,
isMonoforum: true,
topicId: chatPresentationInterfaceState.chatLocation.threadId,
togglePanel: { [weak interfaceInteraction] in
interfaceInteraction?.toggleChatSidebarMode()
},
updateTopicId: { [weak interfaceInteraction] topicId in
interfaceInteraction?.updateChatLocationThread(topicId)
}
))
)
}
} else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, chatPresentationInterfaceState.search == nil {
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
if case .side = topicListDisplayMode {
return AnyComponentWithIdentity(
id: "topics",
component: AnyComponent(ChatSideTopicsPanel(
context: context,
theme: chatPresentationInterfaceState.theme,
strings: chatPresentationInterfaceState.strings,
peerId: peerId,
isMonoforum: false,
topicId: chatPresentationInterfaceState.chatLocation.threadId, topicId: chatPresentationInterfaceState.chatLocation.threadId,
togglePanel: { [weak interfaceInteraction] in togglePanel: { [weak interfaceInteraction] in
interfaceInteraction?.toggleChatSidebarMode() interfaceInteraction?.toggleChatSidebarMode()

View File

@ -517,12 +517,13 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
if self.currentLayout?.0 != width || self.currentLayout?.1 != leftInset || self.currentLayout?.2 != rightInset || messageUpdated || themeUpdated || currentTranslateToLanguageUpdated { if self.currentLayout?.0 != width || self.currentLayout?.1 != leftInset || self.currentLayout?.2 != rightInset || messageUpdated || themeUpdated || currentTranslateToLanguageUpdated {
self.currentLayout = (width, leftInset, rightInset) self.currentLayout = (width, leftInset, rightInset)
let messageUpdated = self.currentMessage?.message.id != interfaceState.pinnedMessage?.message.id
let previousMessageWasNil = self.currentMessage == nil let previousMessageWasNil = self.currentMessage == nil
self.currentMessage = interfaceState.pinnedMessage self.currentMessage = interfaceState.pinnedMessage
if let currentMessage = self.currentMessage, let currentLayout = self.currentLayout { if let currentMessage = self.currentMessage, let currentLayout = self.currentLayout {
self.dustNode?.update(revealed: false, animated: false) self.dustNode?.update(revealed: false, animated: false)
self.enqueueTransition(width: currentLayout.0, panelHeight: panelHeight, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, dateTimeFormat: interfaceState.dateTimeFormat, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread, translateToLanguage: translateToLanguage?.toLang) self.enqueueTransition(width: currentLayout.0, panelHeight: panelHeight, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: messageUpdated ? .immediate : transition, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, dateTimeFormat: interfaceState.dateTimeFormat, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread, translateToLanguage: translateToLanguage?.toLang)
} }
} }
@ -565,6 +566,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
self.textNode.textNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -offset), to: CGPoint(), duration: 0.2, additive: true) self.textNode.textNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -offset), to: CGPoint(), duration: 0.2, additive: true)
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} }
} else {
animationTransition = transition
} }
let makeTitleLayout = self.titleNode.asyncLayout() let makeTitleLayout = self.titleNode.asyncLayout()
@ -575,13 +578,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
let previousMediaReference = self.previousMediaReference let previousMediaReference = self.previousMediaReference
let context = self.context let context = self.context
let targetQueue: Queue
if firstTime {
targetQueue = Queue.mainQueue()
} else {
targetQueue = self.queue
}
let contentLeftInset: CGFloat = leftInset + 10.0 let contentLeftInset: CGFloat = leftInset + 10.0
var textLineInset: CGFloat = 10.0 var textLineInset: CGFloat = 10.0
var rightInset: CGFloat = 14.0 + rightInset var rightInset: CGFloat = 14.0 + rightInset
@ -592,7 +588,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
rightInset += self.actionButton.bounds.width - 14.0 rightInset += self.actionButton.bounds.width - 14.0
} }
targetQueue.async { [weak self] in
var updatedMediaReference: AnyMediaReference? var updatedMediaReference: AnyMediaReference?
var imageDimensions: CGSize? var imageDimensions: CGSize?
@ -779,8 +774,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
spoilerTextLayoutAndApply = nil spoilerTextLayoutAndApply = nil
} }
Queue.mainQueue().async { let strongSelf = self
if let strongSelf = self {
let _ = titleApply(animation != nil) let _ = titleApply(animation != nil)
var textArguments: TextNodeWithEntities.Arguments? var textArguments: TextNodeWithEntities.Arguments?
@ -878,9 +872,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
strongSelf.fetchDisposable.set(updatedFetchMediaSignal.startStrict()) strongSelf.fetchDisposable.set(updatedFetchMediaSignal.startStrict())
} }
} }
}
}
}
@objc func tapped() { @objc func tapped() {
if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage { if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage {

View File

@ -246,7 +246,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
private let containerButton: HighlightTrackingButton private let containerButton: HighlightTrackingButton
private let icon = ComponentView<Empty>() private var icon: ComponentView<Empty>?
private var avatarNode: AvatarNode? private var avatarNode: AvatarNode?
private let title = ComponentView<Empty>() private let title = ComponentView<Empty>()
private var badge: ComponentView<Empty>? private var badge: ComponentView<Empty>?
@ -319,17 +319,22 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
let spacing: CGFloat = 3.0 let spacing: CGFloat = 3.0
let badgeSpacing: CGFloat = 4.0 let badgeSpacing: CGFloat = 4.0
let avatarIconContent: EmojiStatusComponent.Content let iconSize = CGSize(width: 18.0, height: 18.0)
if case let .forum(topicId) = item.item.id, topicId != 1, let threadData = item.item.threadData {
var avatarIconContent: EmojiStatusComponent.Content?
if case let .forum(topicId) = item.item.id {
if topicId != 1, let threadData = item.item.threadData {
if let fileId = threadData.info.icon, fileId != 0 { if let fileId = threadData.info.icon, fileId != 0 {
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0)) avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: iconSize, placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
} else { } else {
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: CGSize(width: 18.0, height: 18.0)) avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize)
} }
} else { } else {
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme)) avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme))
} }
}
if let avatarIconContent {
let avatarIconComponent = EmojiStatusComponent( let avatarIconComponent = EmojiStatusComponent(
context: context, context: context,
animationCache: context.animationCache, animationCache: context.animationCache,
@ -338,14 +343,36 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
isVisibleForAnimations: false, isVisibleForAnimations: false,
action: nil action: nil
) )
let iconSize = self.icon.update( let icon: ComponentView<Empty>
if let current = self.icon {
icon = current
} else {
icon = ComponentView()
self.icon = icon
}
let _ = icon.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(avatarIconComponent), component: AnyComponent(avatarIconComponent),
environment: {}, environment: {},
containerSize: CGSize(width: 18.0, height: 18.0) containerSize: CGSize(width: 18.0, height: 18.0)
) )
} else if let icon = self.icon {
self.icon = nil
icon.view?.removeFromSuperview()
}
let titleText: String = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " " let titleText: String
if case let .forum(topicId) = item.item.id {
let _ = topicId
if let threadData = item.item.threadData {
titleText = threadData.info.title
} else {
//TODO:localize
titleText = "General"
}
} else {
titleText = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
}
let titleSize = self.title.update( let titleSize = self.title.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(MultilineTextComponent( component: AnyComponent(MultilineTextComponent(
@ -391,16 +418,20 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: 5.0 + floor((size.height - iconSize.height) * 0.5)), size: iconSize) let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: 5.0 + floor((size.height - iconSize.height) * 0.5)), size: iconSize)
let titleFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: 5.0 + floor((size.height - titleSize.height) * 0.5)), size: titleSize) let titleFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: 5.0 + floor((size.height - titleSize.height) * 0.5)), size: titleSize)
if let iconView = self.icon.view { if let icon = self.icon {
if let iconView = icon.view {
if iconView.superview == nil { if iconView.superview == nil {
iconView.isUserInteractionEnabled = false iconView.isUserInteractionEnabled = false
self.containerButton.addSubview(iconView) self.containerButton.addSubview(iconView)
} }
iconView.frame = iconFrame iconView.frame = iconFrame
}
if "".isEmpty { if let avatarNode = self.avatarNode {
iconView.isHidden = true self.avatarNode = nil
avatarNode.view.removeFromSuperview()
}
} else {
let avatarNode: AvatarNode let avatarNode: AvatarNode
if let current = self.avatarNode { if let current = self.avatarNode {
avatarNode = current avatarNode = current
@ -419,11 +450,6 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size) avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
} }
} }
} else if let avatarNode = self.avatarNode {
self.avatarNode = nil
avatarNode.view.removeFromSuperview()
iconView.isHidden = false
}
} }
if let titleView = self.title.view { if let titleView = self.title.view {
@ -707,6 +733,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
} }
private let context: AccountContext private let context: AccountContext
private let isMonoforum: Bool
private let scrollView: ScrollView private let scrollView: ScrollView
@ -722,8 +749,9 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
private var appliedScrollToId: ScrollId? private var appliedScrollToId: ScrollId?
init(context: AccountContext, peerId: EnginePeer.Id) { init(context: AccountContext, peerId: EnginePeer.Id, isMonoforum: Bool) {
self.context = context self.context = context
self.isMonoforum = isMonoforum
self.selectedLineView = UIImageView() self.selectedLineView = UIImageView()
@ -751,78 +779,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
self.scrollView.disablesInteractiveTransitionGestureRecognizer = true self.scrollView.disablesInteractiveTransitionGestureRecognizer = true
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId) let threadListSignal: Signal<EngineChatList, NoError> = context.sharedContext.subscribeChatListData(context: context, location: isMonoforum ? .savedMessagesChats(peerId: peerId) : .forum(peerId: peerId))
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: peerId)
let accountPeerId = context.account.peerId
let threadListSignal: Signal<EngineChatList, NoError> = context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey])
|> map { views -> EngineChatList in
guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else {
preconditionFailure()
}
var draft: EngineChatList.Draft?
if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView {
if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp {
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
if let text = opaqueState.synchronizeableInputState?.text {
draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? [])
}
}
}
}
var items: [EngineChatList.Item] = []
for item in view.items {
guard let sourcePeer = item.peer else {
continue
}
let sourceId = PeerId(item.id)
var messages: [EngineMessage] = []
if let topMessage = item.topMessage {
messages.append(EngineMessage(topMessage))
}
let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp)
items.append(EngineChatList.Item(
id: .chatList(sourceId),
index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)),
messages: messages,
readCounters: EnginePeerReadCounters(
incomingReadId: 0, outgoingReadId: 0, count: Int32(item.unreadCount), markedUnread: false),
isMuted: false,
draft: sourceId == accountPeerId ? draft : nil,
threadData: nil,
renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)),
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
forumTopicData: nil,
topForumTopicItems: [],
hasFailed: false,
isContact: false,
autoremoveTimeout: nil,
storyStats: nil,
displayAsTopicList: false,
isPremiumRequiredToMessage: false,
mediaDraftContentType: nil
))
}
let list = EngineChatList(
items: items.reversed(),
groupItems: [],
additionalItems: [],
hasEarlier: false,
hasLater: false,
isLoading: view.isLoading
)
return list
}
self.itemsDisposable = (threadListSignal self.itemsDisposable = (threadListSignal
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in |> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
@ -1001,8 +958,12 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
guard let self else { guard let self else {
return return
} }
if case let .forum(topicId) = chatListItem.id {
self.interfaceInteraction?.updateChatLocationThread(topicId)
} else {
let topicId = chatListItem.renderedPeer.peerId.toInt64() let topicId = chatListItem.renderedPeer.peerId.toInt64()
self.interfaceInteraction?.updateChatLocationThread(topicId) self.interfaceInteraction?.updateChatLocationThread(topicId)
}
}, contextGesture: { gesture, sourceNode in }, contextGesture: { gesture, sourceNode in
}) })
self.itemViews[itemId] = itemView self.itemViews[itemId] = itemView
@ -1010,8 +971,10 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
} }
var isSelected = false var isSelected = false
if params.interfaceState.chatLocation.threadId == item.item.renderedPeer.peerId.toInt64() { if case let .forum(topicId) = item.item.id {
isSelected = true isSelected = params.interfaceState.chatLocation.threadId == topicId
} else {
isSelected = params.interfaceState.chatLocation.threadId == item.item.renderedPeer.peerId.toInt64()
} }
let itemSize = itemView.update(context: self.context, item: item, isSelected: isSelected, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate) let itemSize = itemView.update(context: self.context, item: item, isSelected: isSelected, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate)
let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize) let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize)
@ -1100,7 +1063,15 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
public func topicIndex(threadId: Int64?) -> Int? { public func topicIndex(threadId: Int64?) -> Int? {
if let threadId { if let threadId {
if let value = self.items.firstIndex(where: { $0.id == .chatList(PeerId(threadId)) }) { if let value = self.items.firstIndex(where: { item in
if item.id == .chatList(PeerId(threadId)) {
return true
} else if item.id == .forum(threadId) {
return true
} else {
return false
}
}) {
return value + 1 return value + 1
} else { } else {
return nil return nil

View File

@ -805,7 +805,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
self.suspendedNavigationBarLayout = layout self.suspendedNavigationBarLayout = layout
return return
} }
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition) self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: nil, transition: transition)
} }
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
@ -818,7 +818,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
self.suspendNavigationBarLayout = false self.suspendNavigationBarLayout = false
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout { if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition) self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: nil, transition: transition)
} }
} }

View File

@ -2238,6 +2238,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
) )
} }
public func subscribeChatListData(context: AccountContext, location: ChatListControllerLocation) -> Signal<EngineChatList, NoError> {
return chatListViewForLocation(chatListLocation: location, location: .initial(count: 100, filter: nil), account: context.account, shouldLoadCanMessagePeer: false)
|> map { update -> EngineChatList in
return update.list
}
}
public func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? { public func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? {
return nil return nil
} }

View File

@ -1151,7 +1151,7 @@ public class TranslateScreen: ViewController {
layout.statusBarHeight = nil layout.statusBarHeight = nil
self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, transition: transition) self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, additionalCutout: nil, transition: transition)
} }
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {