This commit is contained in:
Ali 2023-05-29 23:09:37 +04:00
parent fba8f6b9f2
commit bfb4c7bbad
7 changed files with 748 additions and 264 deletions

View File

@ -230,8 +230,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return super.displayNode as! ChatListControllerNode return super.displayNode as! ChatListControllerNode
} }
private let headerContentView = ComponentView<Empty>()
fileprivate private(set) var primaryContext: ChatListLocationContext? fileprivate private(set) var primaryContext: ChatListLocationContext?
private let primaryInfoReady = Promise<Bool>() private let primaryInfoReady = Promise<Bool>()
private let mainReady = Promise<Bool>() private let mainReady = Promise<Bool>()
@ -275,8 +273,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
private let isReorderingTabsValue = ValuePromise<Bool>(false) private let isReorderingTabsValue = ValuePromise<Bool>(false)
private var searchContentNode: NavigationBarSearchContentNode?
private let navigationSecondaryContentNode: ASDisplayNode private let navigationSecondaryContentNode: ASDisplayNode
private let tabContainerNode: ChatListFilterTabContainerNode private let tabContainerNode: ChatListFilterTabContainerNode
private var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)? private var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)?
@ -302,7 +298,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
private var powerSavingMonitoringDisposable: Disposable? private var powerSavingMonitoringDisposable: Disposable?
private var storySubscriptions: EngineStorySubscriptions? private(set) var storySubscriptions: EngineStorySubscriptions?
private var storySubscriptionsDisposable: Disposable? private var storySubscriptionsDisposable: Disposable?
private var preloadStorySubscriptionsDisposable: Disposable? private var preloadStorySubscriptionsDisposable: Disposable?
@ -346,7 +342,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.storyListHeight = 0.0 self.storyListHeight = 0.0
super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary, groupCallPanelSource: groupCallPanelSource) super.init(context: context, navigationBarPresentationData: nil, mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary, groupCallPanelSource: groupCallPanelSource)
self.tabBarItemContextActionType = .always self.tabBarItemContextActionType = .always
self.automaticallyControlPresentationContextLayout = false self.automaticallyControlPresentationContextLayout = false
@ -439,9 +435,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.scrollToTop = { [weak self] in self.scrollToTop = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if let searchContentNode = strongSelf.searchContentNode { /*if let searchContentNode = strongSelf.searchContentNode {
searchContentNode.updateExpansionProgress(1.0, animated: true) searchContentNode.updateExpansionProgress(1.0, animated: true)
} }*/
//TODO:scroll to top
strongSelf.chatListDisplayNode.scrollToTop() strongSelf.chatListDisplayNode.scrollToTop()
} }
} }
@ -454,9 +451,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} else { } else {
switch strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.visibleContentOffset() { switch strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.visibleContentOffset() {
case .none, .unknown: case .none, .unknown:
if let searchContentNode = strongSelf.searchContentNode { //TODO:scroll to top
/*if let searchContentNode = strongSelf.searchContentNode {
searchContentNode.updateExpansionProgress(1.0, animated: true) searchContentNode.updateExpansionProgress(1.0, animated: true)
} }*/
strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.scrollToPosition(.top) strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.scrollToPosition(.top)
case let .known(offset): case let .known(offset):
let isFirstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.chatListFilter == strongSelf.chatListDisplayNode.mainContainerNode.availableFilters.first?.filter let isFirstFilter = strongSelf.chatListDisplayNode.effectiveContainerNode.currentItemNode.chatListFilter == strongSelf.chatListDisplayNode.mainContainerNode.availableFilters.first?.filter
@ -474,9 +472,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
strongSelf.selectTab(id: targetTab) strongSelf.selectTab(id: targetTab)
} else { } else {
if let searchContentNode = strongSelf.searchContentNode { //TODO:scroll to top
/*if let searchContentNode = strongSelf.searchContentNode {
searchContentNode.updateExpansionProgress(1.0, animated: true) searchContentNode.updateExpansionProgress(1.0, animated: true)
} }*/
if let inlineStackContainerNode = strongSelf.chatListDisplayNode.inlineStackContainerNode { if let inlineStackContainerNode = strongSelf.chatListDisplayNode.inlineStackContainerNode {
inlineStackContainerNode.currentItemNode.scrollToPosition(.top) inlineStackContainerNode.currentItemNode.scrollToPosition(.top)
} else { } else {
@ -513,25 +512,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}) })
if !previewing { if !previewing {
let placeholder: String /*
let compactPlaceholder: String
var isForum = false
if case .forum = location {
isForum = true
placeholder = self.presentationData.strings.Common_Search
compactPlaceholder = self.presentationData.strings.Common_Search
} else {
placeholder = self.presentationData.strings.DialogList_SearchLabel
compactPlaceholder = self.presentationData.strings.DialogList_SearchLabelCompact
}
self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder, activate: { [weak self] in self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder, activate: { [weak self] in
self?.chatListDisplayNode.mainContainerNode.currentItemNode.cancelTracking() self?.chatListDisplayNode.mainContainerNode.currentItemNode.cancelTracking()
self?.activateSearch(filter: isForum ? .topics : .chats) self?.activateSearch(filter: isForum ? .topics : .chats)
}) })
self.searchContentNode?.updateExpansionProgress(0.0) self.searchContentNode?.updateExpansionProgress(0.0)
self.navigationBar?.setContentNode(self.searchContentNode, animated: false) self.navigationBar?.setContentNode(self.searchContentNode, animated: false)*/
let tabsIsEmpty: Bool let tabsIsEmpty: Bool
if let (resolvedItems, displayTabsAtBottom, _) = self.tabContainerData { if let (resolvedItems, displayTabsAtBottom, _) = self.tabContainerData {
@ -794,7 +781,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
))) )))
])) ]))
strongSelf.searchContentNode?.placeholderNode.setAccessoryComponent(component: AnyComponent(Button( let _ = contentComponent
//TODO:download indicator
/*strongSelf.searchContentNode?.placeholderNode.setAccessoryComponent(component: AnyComponent(Button(
content: contentComponent, content: contentComponent,
action: { action: {
guard let strongSelf = self else { guard let strongSelf = self else {
@ -802,9 +791,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
strongSelf.activateSearch(filter: .downloads, query: nil) strongSelf.activateSearch(filter: .downloads, query: nil)
} }
))) )))*/
} else { } else {
strongSelf.searchContentNode?.placeholderNode.setAccessoryComponent(component: nil) //strongSelf.searchContentNode?.placeholderNode.setAccessoryComponent(component: nil)
} }
}) })
} }
@ -889,14 +878,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
func findTitleView() -> ChatListTitleView? { func findTitleView() -> ChatListTitleView? {
guard let componentView = self.headerContentView.view as? ChatListHeaderComponent.View else { guard let componentView = self.chatListHeaderView() else {
return nil return nil
} }
return componentView.findTitleView() return componentView.findTitleView()
} }
private var previousEmojiSetupTimestamp: Double? private var previousEmojiSetupTimestamp: Double?
private func openStatusSetup(sourceView: UIView) { func openStatusSetup(sourceView: UIView) {
let currentTimestamp = CACurrentMediaTime() let currentTimestamp = CACurrentMediaTime()
if let previousTimestamp = self.previousEmojiSetupTimestamp, currentTimestamp < previousTimestamp + 1.0 { if let previousTimestamp = self.previousEmojiSetupTimestamp, currentTimestamp < previousTimestamp + 1.0 {
return return
@ -962,40 +951,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.navigationItem.backBarButtonItem = backBarButtonItem self.navigationItem.backBarButtonItem = backBarButtonItem
} }
let placeholder: String
let compactPlaceholder: String
if case .forum = location {
placeholder = self.presentationData.strings.Common_Search
compactPlaceholder = self.presentationData.strings.Common_Search
} else {
placeholder = self.presentationData.strings.DialogList_SearchLabel
compactPlaceholder = self.presentationData.strings.DialogList_SearchLabelCompact
}
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder)
/*let editing = self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing
if case .chatList(.root) = self.location {
self.primaryContext?.leftButton = AnyComponentWithIdentity(id: "edit", component: AnyComponent(NavigationButtonComponent(
content: .text(title: self.presentationData.strings.Common_Edit, isBold: false),
pressed: { [weak self] in
self?.editPressed()
}
)))
self.primaryContext?.rightButton = AnyComponentWithIdentity(id: "compose", component: AnyComponent(NavigationButtonComponent(
content: .icon(imageName: "Chat List/Compose Icon"),
pressed: { [weak self] in
self?.composePressed()
}
)))
} else {
self.primaryContext?.rightButton = AnyComponentWithIdentity(id: "edit", component: AnyComponent(NavigationButtonComponent(
content: .text(title: self.presentationData.strings.Common_Edit, isBold: false),
pressed: { [weak self] in
self?.editPressed()
}
)))
}*/
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
@ -1007,7 +962,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.chatListDisplayNode.updatePresentationData(self.presentationData) self.chatListDisplayNode.updatePresentationData(self.presentationData)
} }
self.requestUpdateHeaderContent(transition: .immediate) self.requestLayout(transition: .immediate)
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
@ -1299,23 +1254,38 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
navigationController.filterController(strongSelf, animated: true) navigationController.filterController(strongSelf, animated: true)
} }
self.chatListDisplayNode.contentOffsetChanged = { [weak self] offset in /*self.chatListDisplayNode.contentOffsetChanged = { [weak self] offset in
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode, let validLayout = strongSelf.validLayout { if let strongSelf = self, let validLayout = strongSelf.validLayout {
var offset = offset var offset = offset
if validLayout.inVoiceOver { if validLayout.inVoiceOver {
offset = .known(0.0) offset = .known(0.0)
} }
//print("offset: \(offset), additionalHeight: \(searchContentNode.additionalHeight)")
searchContentNode.updateListVisibleContentOffset(offset, transition: strongSelf.chatListDisplayNode.temporaryContentOffsetChangeTransition ?? .immediate) let offsetValue: CGFloat
switch offset {
case .none:
offsetValue = 0.0
case let .known(value):
offsetValue = value
case .unknown:
offsetValue = 10000.0
}
if let navigationBarView = strongSelf.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
navigationBarView.applyScroll(offset: offsetValue, transition: .immediate)
}
} }
} }*/
self.chatListDisplayNode.contentScrollingEnded = { [weak self] listView in self.chatListDisplayNode.contentScrollingEnded = { [weak self] listView in
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode { let _ = self
/*if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
return fixListNodeScrolling(listView, searchNode: searchContentNode) return fixListNodeScrolling(listView, searchNode: searchContentNode)
} else { } else {
return false return false
} }*/
//TODO:fix scrolling
return false
} }
self.chatListDisplayNode.emptyListAction = { [weak self] _ in self.chatListDisplayNode.emptyListAction = { [weak self] _ in
@ -1851,7 +1821,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
strongSelf.chatListDisplayNode.isReorderingFilters = true strongSelf.chatListDisplayNode.isReorderingFilters = true
strongSelf.isReorderingTabsValue.set(true) strongSelf.isReorderingTabsValue.set(true)
strongSelf.searchContentNode?.setIsEnabled(false, animated: true)
//TODO:update search enabled
//strongSelf.searchContentNode?.setIsEnabled(false, animated: true)
(strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(false, transition: .animated(duration: 0.2, curve: .easeInOut)) (strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(false, transition: .animated(duration: 0.2, curve: .easeInOut))
if let layout = strongSelf.validLayout { if let layout = strongSelf.validLayout {
strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
@ -1934,6 +1907,22 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.storySubscriptions = storySubscriptions self.storySubscriptions = storySubscriptions
let isEmpty = storySubscriptions.items.isEmpty let isEmpty = storySubscriptions.items.isEmpty
self.storyListHeight = isEmpty ? 0.0 : 94.0
let transition: ContainedViewLayoutTransition
if self.didAppear {
transition = .animated(duration: 0.4, curve: .spring)
} else {
transition = .immediate
}
let _ = wasEmpty
let _ = isEmpty
self.chatListDisplayNode.temporaryContentOffsetChangeTransition = transition
self.requestLayout(transition: transition)
self.chatListDisplayNode.temporaryContentOffsetChangeTransition = nil
self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in
var chatListState = chatListState var chatListState = chatListState
@ -1951,23 +1940,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return chatListState return chatListState
} }
self.storyListHeight = isEmpty ? 0.0 : 94.0
let transition: ContainedViewLayoutTransition
if self.didAppear {
transition = .animated(duration: 0.4, curve: .spring)
} else {
transition = .immediate
}
if wasEmpty != isEmpty {
self.chatListDisplayNode.temporaryContentOffsetChangeTransition = transition
self.requestLayout(transition: transition)
self.chatListDisplayNode.temporaryContentOffsetChangeTransition = nil
} else {
self.requestUpdateHeaderContent(transition: transition)
}
self.storiesReady.set(.single(true)) self.storiesReady.set(.single(true))
}) })
} }
@ -2251,7 +2223,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
} }
if hasEmptyMark { if hasEmptyMark {
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View { if let componentView = self.chatListHeaderView() {
if let rightButtonView = componentView.rightButtonView { if let rightButtonView = componentView.rightButtonView {
let absoluteFrame = rightButtonView.convert(rightButtonView.bounds, to: self.view) let absoluteFrame = rightButtonView.convert(rightButtonView.bounds, to: self.view)
let text: String = self.presentationData.strings.ChatList_EmptyListTooltip let text: String = self.presentationData.strings.ChatList_EmptyListTooltip
@ -2402,13 +2374,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
} }
func requestUpdateHeaderContent(transition: ContainedViewLayoutTransition) { func updateHeaderContent(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> (primaryContent: ChatListHeaderComponent.Content?, secondaryContent: ChatListHeaderComponent.Content?) {
if let validLayout = self.validLayout {
self.updateHeaderContent(layout: validLayout, transition: transition)
}
}
private func updateHeaderContent(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
var primaryContent: ChatListHeaderComponent.Content? var primaryContent: ChatListHeaderComponent.Content?
if let primaryContext = self.primaryContext { if let primaryContext = self.primaryContext {
var backTitle: String? var backTitle: String?
@ -2459,8 +2425,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
) )
} }
var storiesFraction: CGFloat = 0.0 return (primaryContent, secondaryContent)
if let searchContentNode = self.searchContentNode, case .chatList(.root) = self.location {
/*let storiesFraction: CGFloat = 0.0
//TODO:move to navigation bar
/*if let searchContentNode = self.searchContentNode, case .chatList(.root) = self.location {
if self.storyListHeight > 0.0 { if self.storyListHeight > 0.0 {
let fraction = navigationBarSearchContentHeight / searchContentNode.nominalHeight let fraction = navigationBarSearchContentHeight / searchContentNode.nominalHeight
@ -2471,7 +2440,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let visibleProgress: CGFloat = toLow + (searchContentNode.expansionProgress - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) let visibleProgress: CGFloat = toLow + (searchContentNode.expansionProgress - fromLow) * (toHigh - toLow) / (fromHigh - fromLow)
storiesFraction = max(0.0, min(1.0, visibleProgress)) storiesFraction = max(0.0, min(1.0, visibleProgress))
} }
} }*/
let _ = self.headerContentView.update( let _ = self.headerContentView.update(
transition: Transition(transition), transition: Transition(transition),
@ -2506,107 +2475,27 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if self.navigationBar?.customHeaderContentView !== componentView { if self.navigationBar?.customHeaderContentView !== componentView {
self.navigationBar?.customHeaderContentView = componentView self.navigationBar?.customHeaderContentView = componentView
} }
} }*/
if case .chatList(.root) = self.location {
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View {
componentView.storyPeerAction = { [weak self] peer in
guard let self else {
return
}
let storyContent = StoryContentContextImpl(context: self.context, focusedPeerId: peer?.id)
let _ = (storyContent.state
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] storyContentState in
guard let self else {
return
}
if let peer, peer.id == self.context.account.peerId, storyContentState.slice == nil {
var cameraTransitionIn: StoryCameraTransitionIn?
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
cameraTransitionIn = StoryCameraTransitionIn(
sourceView: transitionView,
sourceRect: transitionView.bounds,
sourceCornerRadius: transitionView.bounds.height * 0.5
)
}
}
if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
rootController.openStoryCamera(transitionIn: cameraTransitionIn, transitionOut: { [weak self] _ in
guard let self else {
return nil
}
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
return StoryCameraTransitionOut(
destinationView: transitionView,
destinationRect: transitionView.bounds,
destinationCornerRadius: transitionView.bounds.height * 0.5
)
}
}
return nil
})
}
return
}
var transitionIn: StoryContainerScreen.TransitionIn?
if let peer, let componentView = self.headerContentView.view as? ChatListHeaderComponent.View {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peer.id) {
transitionIn = StoryContainerScreen.TransitionIn(
sourceView: transitionView,
sourceRect: transitionView.bounds,
sourceCornerRadius: transitionView.bounds.height * 0.5
)
}
}
let storyContainerScreen = StoryContainerScreen(
context: self.context,
content: storyContent,
transitionIn: transitionIn,
transitionOut: { [weak self] peerId, _ in
guard let self else {
return nil
}
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) {
return StoryContainerScreen.TransitionOut(
destinationView: transitionView,
destinationRect: transitionView.bounds,
destinationCornerRadius: transitionView.bounds.height * 0.5,
destinationIsAvatar: true,
completed: {}
)
}
}
return nil
}
)
self.push(storyContainerScreen)
})
}
}
}
} }
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.updateHeaderContent(layout: layout, transition: transition)
super.updateNavigationBarLayout(layout, transition: transition) super.updateNavigationBarLayout(layout, transition: transition)
} }
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { private func chatListHeaderView() -> ChatListHeaderComponent.View? {
if case .chatList(.root) = self.location, !self.isSearchActive { if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
self.searchContentNode?.additionalHeight = (1.0 - self.chatListDisplayNode.inlineStackContainerTransitionFraction) * self.storyListHeight if let componentView = navigationBarView.headerContent.view as? ChatListHeaderComponent.View {
return componentView
}
} }
return nil
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
//TODO:move to chat list node
/*if case .chatList(.root) = self.location, !self.isSearchActive {
self.searchContentNode?.additionalHeight = (1.0 - self.chatListDisplayNode.inlineStackContainerTransitionFraction) * self.storyListHeight
}*/
super.containerLayoutUpdated(layout, transition: transition) super.containerLayoutUpdated(layout, transition: transition)
@ -2616,14 +2505,100 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.updateLayout(layout: layout, transition: transition) self.updateLayout(layout: layout, transition: transition)
if let searchContentNode = self.searchContentNode, layout.inVoiceOver != wasInVoiceOver { if layout.inVoiceOver != wasInVoiceOver {
searchContentNode.updateListVisibleContentOffset(.known(0.0))
self.chatListDisplayNode.scrollToTop() self.chatListDisplayNode.scrollToTop()
} }
if case .chatList(.root) = self.location, let componentView = self.chatListHeaderView() {
componentView.storyPeerAction = { [weak self] peer in
guard let self else {
return
}
let storyContent = StoryContentContextImpl(context: self.context, focusedPeerId: peer?.id)
let _ = (storyContent.state
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] storyContentState in
guard let self else {
return
}
if let peer, peer.id == self.context.account.peerId, storyContentState.slice == nil {
var cameraTransitionIn: StoryCameraTransitionIn?
if let componentView = self.chatListHeaderView() {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
cameraTransitionIn = StoryCameraTransitionIn(
sourceView: transitionView,
sourceRect: transitionView.bounds,
sourceCornerRadius: transitionView.bounds.height * 0.5
)
}
}
if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
rootController.openStoryCamera(transitionIn: cameraTransitionIn, transitionOut: { [weak self] _ in
guard let self else {
return nil
}
if let componentView = self.chatListHeaderView() {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
return StoryCameraTransitionOut(
destinationView: transitionView,
destinationRect: transitionView.bounds,
destinationCornerRadius: transitionView.bounds.height * 0.5
)
}
}
return nil
})
}
return
}
var transitionIn: StoryContainerScreen.TransitionIn?
if let peer, let componentView = self.chatListHeaderView() {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peer.id) {
transitionIn = StoryContainerScreen.TransitionIn(
sourceView: transitionView,
sourceRect: transitionView.bounds,
sourceCornerRadius: transitionView.bounds.height * 0.5
)
}
}
let storyContainerScreen = StoryContainerScreen(
context: self.context,
content: storyContent,
transitionIn: transitionIn,
transitionOut: { [weak self] peerId, _ in
guard let self else {
return nil
}
if let componentView = self.chatListHeaderView() {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) {
return StoryContainerScreen.TransitionOut(
destinationView: transitionView,
destinationRect: transitionView.bounds,
destinationCornerRadius: transitionView.bounds.height * 0.5,
destinationIsAvatar: true,
completed: {}
)
}
}
return nil
}
)
self.push(storyContainerScreen)
})
}
}
} }
public func transitionViewForOwnStoryItem() -> UIView? { public func transitionViewForOwnStoryItem() -> UIView? {
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View { if let componentView = self.chatListHeaderView() {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) { if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
return transitionView return transitionView
} }
@ -2632,7 +2607,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
public func animateStoryUploadRipple() { public func animateStoryUploadRipple() {
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View { if let componentView = self.chatListHeaderView() {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) { if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
let localRect = transitionView.convert(transitionView.bounds, to: self.view) let localRect = transitionView.convert(transitionView.bounds, to: self.view)
self.animateRipple(centerLocation: localRect.center) self.animateRipple(centerLocation: localRect.center)
@ -2669,7 +2644,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
tabContainerOffset += 44.0 + 20.0 tabContainerOffset += 44.0 + 20.0
} }
let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0 let navigationBarHeight: CGFloat = 100.0//self.navigationBar?.frame.maxY ?? 0.0
let secondaryContentHeight = self.navigationBar?.secondaryContentHeight ?? 0.0 let secondaryContentHeight = self.navigationBar?.secondaryContentHeight ?? 0.0
transition.updateFrame(node: self.navigationSecondaryContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - secondaryContentHeight + tabContainerOffset), size: CGSize(width: layout.size.width, height: secondaryContentHeight))) transition.updateFrame(node: self.navigationSecondaryContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - secondaryContentHeight + tabContainerOffset), size: CGSize(width: layout.size.width, height: secondaryContentHeight)))
@ -2680,7 +2655,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
} }
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, visualNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: self.cleanNavigationHeight, storiesInset: self.storyListHeight, transition: transition) self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: navigationBarHeight, storiesInset: self.storyListHeight, transition: transition)
} }
override public func navigationStackConfigurationUpdated(next: [ViewController]) { override public func navigationStackConfigurationUpdated(next: [ViewController]) {
@ -2708,9 +2683,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
} }
self.requestUpdateHeaderContent(transition: .animated(duration: 0.3, curve: .spring)) //TODO:update search enabled
//self.searchContentNode?.setIsEnabled(false, animated: true)
self.searchContentNode?.setIsEnabled(false, animated: true)
self.chatListDisplayNode.didBeginSelectingChatsWhileEditing = false self.chatListDisplayNode.didBeginSelectingChatsWhileEditing = false
self.chatListDisplayNode.effectiveContainerNode.updateState { state in self.chatListDisplayNode.effectiveContainerNode.updateState { state in
@ -2729,7 +2703,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let skipLayoutUpdate = self.reorderingDonePressed() let skipLayoutUpdate = self.reorderingDonePressed()
(self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(nil, transition: .animated(duration: 0.4, curve: .spring)) (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(nil, transition: .animated(duration: 0.4, curve: .spring))
self.searchContentNode?.setIsEnabled(true, animated: true)
//TODO:update search enabled
//self.searchContentNode?.setIsEnabled(true, animated: true)
self.chatListDisplayNode.didBeginSelectingChatsWhileEditing = false self.chatListDisplayNode.didBeginSelectingChatsWhileEditing = false
self.chatListDisplayNode.effectiveContainerNode.updateState { state in self.chatListDisplayNode.effectiveContainerNode.updateState { state in
var state = state var state = state
@ -2776,7 +2753,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
strongSelf.chatListDisplayNode.isReorderingFilters = false strongSelf.chatListDisplayNode.isReorderingFilters = false
strongSelf.isReorderingTabsValue.set(false) strongSelf.isReorderingTabsValue.set(false)
(strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(true, transition: .animated(duration: 0.2, curve: .easeInOut)) (strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(true, transition: .animated(duration: 0.2, curve: .easeInOut))
strongSelf.searchContentNode?.setIsEnabled(true, animated: true)
//TODO:update search enabled
//strongSelf.searchContentNode?.setIsEnabled(true, animated: true)
if let layout = strongSelf.validLayout { if let layout = strongSelf.validLayout {
strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
} }
@ -3446,24 +3426,43 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
public private(set) var isSearchActive: Bool = false public private(set) var isSearchActive: Bool = false
public func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil) {
self.activateSearch(filter: filter, query: query, skipScrolling: false) public func activateSearch(filter: ChatListSearchFilter, query: String? = nil) {
var searchContentNode: NavigationBarSearchContentNode?
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
searchContentNode = navigationBarView.searchContentNode
}
if let searchContentNode {
self.activateSearch(filter: filter, query: query, skipScrolling: false, searchContentNode: searchContentNode)
}
}
public func activateSearch(query: String? = nil) {
var isForum = false
if case .forum = self.location {
isForum = true
}
let filter: ChatListSearchFilter = isForum ? .topics : .chats
self.activateSearch(filter: filter, query: query)
} }
private func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil, skipScrolling: Bool = false) { func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil, skipScrolling: Bool = false, searchContentNode: NavigationBarSearchContentNode) {
var filter = filter var filter = filter
if case .forum = self.chatListDisplayNode.effectiveContainerNode.location { if case .forum = self.chatListDisplayNode.effectiveContainerNode.location {
filter = .topics filter = .topics
} }
if self.displayNavigationBar { if self.chatListDisplayNode.searchDisplayController == nil {
if !skipScrolling, let searchContentNode = self.searchContentNode, searchContentNode.expansionProgress != 1.0 { /*if !skipScrolling, let searchContentNode = self.searchContentNode, searchContentNode.expansionProgress != 1.0 {
self.scrollToTop?() self.scrollToTop?()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in
self?.activateSearch(filter: filter, query: query, skipScrolling: true) self?.activateSearch(filter: filter, query: query, skipScrolling: true)
}) })
return return
} }*/
//TODO:scroll to top?
let _ = (combineLatest(self.chatListDisplayNode.mainContainerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1)) let _ = (combineLatest(self.chatListDisplayNode.mainContainerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1))
|> deliverOnMainQueue).start(next: { [weak self] _, chatListView in |> deliverOnMainQueue).start(next: { [weak self] _, chatListView in
@ -3492,28 +3491,26 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}) })
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -strongSelf.storyListHeight), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -strongSelf.storyListHeight), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
} }
if let searchContentNode = strongSelf.searchContentNode { if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: strongSelf.hasDownloads, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) {
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: strongSelf.hasDownloads, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) { let (filterContainerNode, activate) = filterContainerNodeAndActivate
let (filterContainerNode, activate) = filterContainerNodeAndActivate if displaySearchFilters {
if displaySearchFilters { //TODO:search filters
strongSelf.navigationBar?.secondaryContentHeight = NavigationBar.defaultSecondaryContentHeight strongSelf.navigationBar?.secondaryContentHeight = NavigationBar.defaultSecondaryContentHeight
strongSelf.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: false) strongSelf.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: false)
} }
strongSelf.searchContentNode?.additionalHeight = 0.0
activate(filter != .downloads)
activate(filter != .downloads)
if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode {
if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { searchContentNode.search(filter: filter, query: query)
searchContentNode.search(filter: filter, query: query) }
}
let tabsOffset = 30.0 + strongSelf.storyListHeight
let tabsOffset = 30.0 + strongSelf.storyListHeight
Queue.mainQueue().justDispatch {
Queue.mainQueue().justDispatch { filterContainerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: tabsOffset), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
filterContainerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: tabsOffset), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true) filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
} }
} }
@ -3552,6 +3549,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
var filterContainerNode: ASDisplayNode? var filterContainerNode: ASDisplayNode?
var searchContentNode: NavigationBarSearchContentNode?
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
searchContentNode = navigationBarView.searchContentNode
}
if animated, let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { if animated, let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode {
filterContainerNode = searchContentNode.filterContainerNode filterContainerNode = searchContentNode.filterContainerNode
@ -3573,7 +3576,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
} }
if let searchContentNode = self.searchContentNode { if let searchContentNode {
let previousFrame = searchContentNode.placeholderNode.frame let previousFrame = searchContentNode.placeholderNode.frame
if case .chatList(.root) = self.location { if case .chatList(.root) = self.location {
searchContentNode.placeholderNode.frame = previousFrame.offsetBy(dx: 0.0, dy: 94.0) searchContentNode.placeholderNode.frame = previousFrame.offsetBy(dx: 0.0, dy: 94.0)
@ -3582,10 +3585,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
searchContentNode.placeholderNode.frame = previousFrame searchContentNode.placeholderNode.frame = previousFrame
} }
self.requestLayout(transition: .animated(duration: 0.5, curve: .spring))
self.navigationBar?.secondaryContentHeight = (!tabsIsEmpty ? NavigationBar.defaultSecondaryContentHeight : 0.0) self.navigationBar?.secondaryContentHeight = (!tabsIsEmpty ? NavigationBar.defaultSecondaryContentHeight : 0.0)
if case .chatList(.root) = self.location { //TODO:move layout to navigation bar
/*if case .chatList(.root) = self.location {
self.searchContentNode?.additionalHeight = self.storyListHeight self.searchContentNode?.additionalHeight = self.storyListHeight
} }*/
self.navigationBar?.setSecondaryContentNode(self.navigationSecondaryContentNode, animated: false) self.navigationBar?.setSecondaryContentNode(self.navigationSecondaryContentNode, animated: false)
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate
@ -5539,7 +5545,7 @@ private final class ChatListLocationContext {
self.ready.set(.single(true)) self.ready.set(.single(true))
} }
self.parentController?.requestUpdateHeaderContent(transition: .immediate) self.parentController?.requestLayout(transition: .immediate)
} }
private func updateForum( private func updateForum(
@ -5631,7 +5637,7 @@ private final class ChatListLocationContext {
navigationController.replaceController(parentController, with: chatController, animated: true) navigationController.replaceController(parentController, with: chatController, animated: true)
} }
} else { } else {
self.parentController?.requestUpdateHeaderContent(transition: .immediate) self.parentController?.requestLayout(transition: .immediate)
} }
} }

View File

@ -18,6 +18,7 @@ import ActionPanelComponent
import ComponentDisplayAdapters import ComponentDisplayAdapters
import ComponentFlow import ComponentFlow
import ChatFolderLinkPreviewScreen import ChatFolderLinkPreviewScreen
import ChatListHeaderComponent
public enum ChatListContainerNodeFilter: Equatable { public enum ChatListContainerNodeFilter: Equatable {
case all case all
@ -876,7 +877,11 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
self?.updatePeerGrouping?(peerId, group) self?.updatePeerGrouping?(peerId, group)
} }
itemNode.listNode.contentOffsetChanged = { [weak self] offset in itemNode.listNode.contentOffsetChanged = { [weak self] offset in
self?.contentOffsetChanged?(offset) guard let self else {
return
}
self.contentOffset = offset
self.contentOffsetChanged?(offset)
} }
itemNode.listNode.contentScrollingEnded = { [weak self] listView in itemNode.listNode.contentScrollingEnded = { [weak self] listView in
return self?.contentScrollingEnded?(listView) ?? false return self?.contentScrollingEnded?(listView) ?? false
@ -947,6 +952,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
public var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)? public var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
var groupSelected: ((EngineChatList.Group) -> Void)? var groupSelected: ((EngineChatList.Group) -> Void)?
var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)? var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
var contentOffset: ListViewVisibleContentOffset?
public var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)? public var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
public var contentScrollingEnded: ((ListView) -> Bool)? public var contentScrollingEnded: ((ListView) -> Bool)?
var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
@ -1490,12 +1496,15 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
private var tapRecognizer: UITapGestureRecognizer? private var tapRecognizer: UITapGestureRecognizer?
var navigationBar: NavigationBar? var navigationBar: NavigationBar?
let navigationBarView = ComponentView<Empty>()
weak var controller: ChatListControllerImpl? weak var controller: ChatListControllerImpl?
var toolbar: Toolbar? var toolbar: Toolbar?
private var toolbarNode: ToolbarNode? private var toolbarNode: ToolbarNode?
var toolbarActionSelected: ((ToolbarActionOption) -> Void)? var toolbarActionSelected: ((ToolbarActionOption) -> Void)?
private var isSearchDisplayControllerActive: Bool = false
private var skipSearchDisplayControllerLayout: Bool = false
private(set) var searchDisplayController: SearchDisplayController? private(set) var searchDisplayController: SearchDisplayController?
var isReorderingFilters: Bool = false var isReorderingFilters: Bool = false
@ -1504,7 +1513,6 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
private var containerLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, storiesInset: CGFloat)? private var containerLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, storiesInset: CGFloat)?
var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
var contentScrollingEnded: ((ListView) -> Bool)? var contentScrollingEnded: ((ListView) -> Bool)?
var requestDeactivateSearch: (() -> Void)? var requestDeactivateSearch: (() -> Void)?
@ -1695,7 +1703,105 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
} }
} }
private func updateNavigationBar(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> (navigationHeight: CGFloat, storiesInset: CGFloat) {
let headerContent = self.controller?.updateHeaderContent(layout: layout, transition: transition)
let navigationBarSize = self.navigationBarView.update(
transition: Transition(transition),
component: AnyComponent(ChatListNavigationBar(
context: self.context,
theme: self.presentationData.theme,
strings: self.presentationData.strings,
statusBarHeight: layout.statusBarHeight ?? 0.0,
sideInset: layout.safeInsets.left,
isSearchActive: self.isSearchDisplayControllerActive,
primaryContent: headerContent?.primaryContent,
secondaryContent: headerContent?.secondaryContent,
secondaryTransition: self.inlineStackContainerTransitionFraction,
storySubscriptions: self.controller?.storySubscriptions,
activateSearch: { [weak self] searchContentNode in
guard let self, let controller = self.controller else {
return
}
var isForum = false
if case .forum = controller.location {
isForum = true
}
let filter: ChatListSearchFilter = isForum ? .topics : .chats
controller.activateSearch(
filter: filter,
query: nil,
skipScrolling: false,
searchContentNode: searchContentNode
)
},
openStatusSetup: { [weak self] sourceView in
guard let self, let controller = self.controller else {
return
}
controller.openStatusSetup(sourceView: sourceView)
}
)),
environment: {},
containerSize: layout.size
)
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
navigationBarComponentView.deferScrollApplication = true
if navigationBarComponentView.superview == nil {
self.view.addSubview(navigationBarComponentView)
}
transition.updateFrame(view: navigationBarComponentView, frame: CGRect(origin: CGPoint(), size: navigationBarSize))
return (navigationBarSize.height, navigationBarComponentView.effectiveStoriesInsetHeight)
} else {
return (0.0, 0.0)
}
}
private func updateNavigationScrolling(transition: ContainedViewLayoutTransition) {
let mainOffset: CGFloat
if let contentOffset = self.mainContainerNode.contentOffset, case let .known(value) = contentOffset {
mainOffset = value
} else {
mainOffset = 1000.0
}
let resultingOffset: CGFloat
if let inlineStackContainerNode = self.inlineStackContainerNode {
let inlineOffset: CGFloat
if let contentOffset = inlineStackContainerNode.contentOffset, case let .known(value) = contentOffset {
inlineOffset = value
} else {
inlineOffset = 1000.0
}
resultingOffset = mainOffset * (1.0 - self.inlineStackContainerTransitionFraction) + inlineOffset * self.inlineStackContainerTransitionFraction
} else {
resultingOffset = mainOffset
}
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
navigationBarComponentView.applyScroll(offset: resultingOffset, transition: Transition(transition))
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, storiesInset: CGFloat, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, storiesInset: CGFloat, transition: ContainedViewLayoutTransition) {
var navigationBarHeight = navigationBarHeight
var visualNavigationHeight = visualNavigationHeight
var cleanNavigationBarHeight = cleanNavigationBarHeight
var storiesInset = storiesInset
let navigationBarLayout = self.updateNavigationBar(layout: layout, transition: transition)
navigationBarHeight = navigationBarLayout.navigationHeight
visualNavigationHeight = navigationBarLayout.navigationHeight
cleanNavigationBarHeight = navigationBarLayout.navigationHeight
storiesInset = navigationBarLayout.storiesInset
self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, storiesInset) self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, storiesInset)
var insets = layout.insets(options: [.input]) var insets = layout.insets(options: [.input])
@ -1789,7 +1895,9 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
var inlineInsets = insets var inlineInsets = insets
inlineInsets.left = 0.0 inlineInsets.left = 0.0
inlineStackContainerNode.update(layout: inlineLayout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: inlineInsets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0, storiesInset: storiesInset, transition: inlineStackContainerNodeTransition) let inlineNavigationHeight: CGFloat = navigationBarLayout.navigationHeight - navigationBarLayout.storiesInset
inlineStackContainerNode.update(layout: inlineLayout, navigationBarHeight: inlineNavigationHeight, visualNavigationHeight: inlineNavigationHeight, originalNavigationHeight: inlineNavigationHeight, cleanNavigationBarHeight: inlineNavigationHeight, insets: inlineInsets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, inlineNavigationLocation: nil, inlineNavigationTransitionFraction: 0.0, storiesInset: storiesInset, transition: inlineStackContainerNodeTransition)
if animateIn { if animateIn {
transition.animatePosition(node: inlineStackContainerNode, from: CGPoint(x: inlineStackContainerNode.position.x + inlineStackContainerNode.bounds.width + UIScreenPixel, y: inlineStackContainerNode.position.y)) transition.animatePosition(node: inlineStackContainerNode, from: CGPoint(x: inlineStackContainerNode.position.x + inlineStackContainerNode.bounds.width + UIScreenPixel, y: inlineStackContainerNode.position.y))
@ -1799,12 +1907,21 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.tapRecognizer?.isEnabled = self.isReorderingFilters self.tapRecognizer?.isEnabled = self.isReorderingFilters
if let searchDisplayController = self.searchDisplayController { if let searchDisplayController = self.searchDisplayController {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition) if !self.skipSearchDisplayControllerLayout {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition)
}
}
self.updateNavigationScrolling(transition: transition)
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
navigationBarComponentView.deferScrollApplication = false
navigationBarComponentView.applyCurrentScroll(transition: Transition(transition))
} }
} }
func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) -> (ASDisplayNode, (Bool) -> Void)? { func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) -> (ASDisplayNode, (Bool) -> Void)? {
guard let (containerLayout, _, _, cleanNavigationBarHeight, _) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else { guard let (containerLayout, _, _, cleanNavigationBarHeight, _) = self.containerLayout, self.searchDisplayController == nil else {
return nil return nil
} }
@ -1834,7 +1951,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self?.controller?.presentInGlobalOverlay(c, with: a) self?.controller?.presentInGlobalOverlay(c, with: a)
}, navigationController: navigationController) }, navigationController: navigationController)
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: contentNode, cancel: { [weak self] in self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, contentNode: contentNode, cancel: { [weak self] in
if let requestDeactivateSearch = self?.requestDeactivateSearch { if let requestDeactivateSearch = self?.requestDeactivateSearch {
requestDeactivateSearch() requestDeactivateSearch()
} }
@ -1846,29 +1963,42 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.isSearchDisplayControllerActive = true
strongSelf.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: cleanNavigationBarHeight, transition: .immediate) strongSelf.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
strongSelf.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in strongSelf.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in
if let strongSelf = self, let strongPlaceholderNode = placeholderNode { guard let self else {
if isSearchBar { return
strongPlaceholderNode.supernode?.insertSubnode(subnode, aboveSubnode: strongPlaceholderNode) }
} else {
strongSelf.insertSubnode(subnode, belowSubnode: navigationBar) if isSearchBar {
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
navigationBarComponentView.addSubnode(subnode)
} }
} else {
self.insertSubnode(subnode, aboveSubnode: self.debugListView)
} }
}, placeholder: placeholderNode, focus: focus) }, placeholder: placeholderNode, focus: focus)
strongSelf.controller?.requestLayout(transition: .animated(duration: 0.5, curve: .spring))
}) })
} }
func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) -> (() -> Void)? { func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) -> (() -> Void)? {
if let searchDisplayController = self.searchDisplayController { if let searchDisplayController = self.searchDisplayController {
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) self.isSearchDisplayControllerActive = false
self.searchDisplayController = nil self.searchDisplayController = nil
self.mainContainerNode.accessibilityElementsHidden = false self.mainContainerNode.accessibilityElementsHidden = false
self.inlineStackContainerNode?.accessibilityElementsHidden = false self.inlineStackContainerNode?.accessibilityElementsHidden = false
return { [weak self] in return { [weak self] in
if let strongSelf = self, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout { if let strongSelf = self, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout {
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated)
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
strongSelf.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
} }
} }
} else { } else {
@ -1884,15 +2014,17 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
private var contentOffsetSyncLockedIn: Bool = false private var contentOffsetSyncLockedIn: Bool = false
private func contentOffsetChanged(offset: ListViewVisibleContentOffset, isPrimary: Bool) { private func contentOffsetChanged(offset: ListViewVisibleContentOffset, isPrimary: Bool) {
guard let inlineStackContainerNode = self.inlineStackContainerNode else {
self.contentOffsetChanged?(offset)
return
}
guard let containerLayout = self.containerLayout else { guard let containerLayout = self.containerLayout else {
return return
} }
let _ = containerLayout
if let inlineStackContainerNode = self.inlineStackContainerNode {
let _ = inlineStackContainerNode
}
if !isPrimary { self.updateNavigationScrolling(transition: .immediate)
/*if !isPrimary {
self.contentOffsetChanged?(offset) self.contentOffsetChanged?(offset)
if "".isEmpty { if "".isEmpty {
return return
@ -1933,7 +2065,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
if !isPrimary { if !isPrimary {
self.contentOffsetChanged?(offset) self.contentOffsetChanged?(offset)
} }
} }*/
} }
private func contentScrollingEnded(listView: ListView, isPrimary: Bool) -> Bool { private func contentScrollingEnded(listView: ListView, isPrimary: Bool) -> Bool {

View File

@ -3258,7 +3258,6 @@ public final class ChatListNode: ListView {
if let previousStoriesInset = self.previousStoriesInset { if let previousStoriesInset = self.previousStoriesInset {
additionalScrollDistance += previousStoriesInset - storiesInset additionalScrollDistance += previousStoriesInset - storiesInset
additionalScrollDistance = 0.0
} }
self.previousStoriesInset = storiesInset self.previousStoriesInset = storiesInset
//print("storiesInset: \(storiesInset), additionalScrollDistance: \(additionalScrollDistance)") //print("storiesInset: \(storiesInset), additionalScrollDistance: \(additionalScrollDistance)")

View File

@ -1012,7 +1012,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
} }
public func animateIn(from node: SearchBarPlaceholderNode, duration: Double, timingFunction: String) { public func animateIn(from node: SearchBarPlaceholderNode, duration: Double, timingFunction: String) {
let initialTextBackgroundFrame = node.convert(node.backgroundNode.frame, to: self) let initialTextBackgroundFrame = node.view.convert(node.backgroundNode.frame, to: self.view)
let initialBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.bounds.size.width, height: max(0.0, initialTextBackgroundFrame.maxY + 8.0))) let initialBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.bounds.size.width, height: max(0.0, initialTextBackgroundFrame.maxY + 8.0)))
if let fromBackgroundColor = node.backgroundColor, let toBackgroundColor = self.backgroundNode.backgroundColor { if let fromBackgroundColor = node.backgroundColor, let toBackgroundColor = self.backgroundNode.backgroundColor {
@ -1060,7 +1060,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
} }
public func transitionOut(to node: SearchBarPlaceholderNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { public func transitionOut(to node: SearchBarPlaceholderNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
let targetTextBackgroundFrame = node.convert(node.backgroundNode.frame, to: self) let targetTextBackgroundFrame = node.view.convert(node.backgroundNode.frame, to: self.view)
let duration: Double = transition.isAnimated ? 0.5 : 0.0 let duration: Double = transition.isAnimated ? 0.5 : 0.0
let timingFunction = kCAMediaTimingFunctionSpring let timingFunction = kCAMediaTimingFunctionSpring

View File

@ -18,7 +18,7 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode {
public var placeholderHeight: CGFloat? public var placeholderHeight: CGFloat?
private var disabledOverlay: ASDisplayNode? private var disabledOverlay: ASDisplayNode?
public private(set) var expansionProgress: CGFloat = 1.0 public var expansionProgress: CGFloat = 1.0
public var additionalHeight: CGFloat = 0.0 public var additionalHeight: CGFloat = 0.0
@ -103,7 +103,7 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode {
private func updatePlaceholder(_ progress: CGFloat, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { private func updatePlaceholder(_ progress: CGFloat, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
let padding: CGFloat = 10.0 let padding: CGFloat = 10.0
let baseWidth = self.bounds.width - padding * 2.0 - leftInset - rightInset let baseWidth = size.width - padding * 2.0 - leftInset - rightInset
let fieldHeight: CGFloat = 36.0 let fieldHeight: CGFloat = 36.0
let fraction = fieldHeight / self.nominalHeight let fraction = fieldHeight / self.nominalHeight

View File

@ -21,6 +21,8 @@ swift_library(
"//submodules/AsyncDisplayKit", "//submodules/AsyncDisplayKit",
"//submodules/AnimationUI", "//submodules/AnimationUI",
"//submodules/TelegramUI/Components/Stories/StoryPeerListComponent", "//submodules/TelegramUI/Components/Stories/StoryPeerListComponent",
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/SearchUI",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -0,0 +1,345 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import ComponentFlow
import TelegramPresentationData
import ComponentDisplayAdapters
import SearchUI
import AccountContext
import TelegramCore
public final class ChatListNavigationBar: Component {
public let context: AccountContext
public let theme: PresentationTheme
public let strings: PresentationStrings
public let statusBarHeight: CGFloat
public let sideInset: CGFloat
public let isSearchActive: Bool
public let primaryContent: ChatListHeaderComponent.Content?
public let secondaryContent: ChatListHeaderComponent.Content?
public let secondaryTransition: CGFloat
public let storySubscriptions: EngineStorySubscriptions?
public let activateSearch: (NavigationBarSearchContentNode) -> Void
public let openStatusSetup: (UIView) -> Void
public init(
context: AccountContext,
theme: PresentationTheme,
strings: PresentationStrings,
statusBarHeight: CGFloat,
sideInset: CGFloat,
isSearchActive: Bool,
primaryContent: ChatListHeaderComponent.Content?,
secondaryContent: ChatListHeaderComponent.Content?,
secondaryTransition: CGFloat,
storySubscriptions: EngineStorySubscriptions?,
activateSearch: @escaping (NavigationBarSearchContentNode) -> Void,
openStatusSetup: @escaping (UIView) -> Void
) {
self.context = context
self.theme = theme
self.strings = strings
self.statusBarHeight = statusBarHeight
self.sideInset = sideInset
self.isSearchActive = isSearchActive
self.primaryContent = primaryContent
self.secondaryContent = secondaryContent
self.secondaryTransition = secondaryTransition
self.storySubscriptions = storySubscriptions
self.activateSearch = activateSearch
self.openStatusSetup = openStatusSetup
}
public static func ==(lhs: ChatListNavigationBar, rhs: ChatListNavigationBar) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.statusBarHeight != rhs.statusBarHeight {
return false
}
if lhs.sideInset != rhs.sideInset {
return false
}
if lhs.isSearchActive != rhs.isSearchActive {
return false
}
if lhs.primaryContent != rhs.primaryContent {
return false
}
if lhs.secondaryContent != rhs.secondaryContent {
return false
}
if lhs.secondaryTransition != rhs.secondaryTransition {
return false
}
if lhs.storySubscriptions != rhs.storySubscriptions {
return false
}
return true
}
private struct CurrentLayout {
var size: CGSize
init(size: CGSize) {
self.size = size
}
}
public final class View: UIView {
private let backgroundView: BlurredBackgroundView
private let separatorLayer: SimpleLayer
public let headerContent = ComponentView<Empty>()
public private(set) var searchContentNode: NavigationBarSearchContentNode?
private var component: ChatListNavigationBar?
private weak var state: EmptyComponentState?
private var scrollTheme: PresentationTheme?
private var scrollStrings: PresentationStrings?
private var currentLayout: CurrentLayout?
private var rawScrollOffset: CGFloat?
private var clippedScrollOffset: CGFloat?
public var deferScrollApplication: Bool = false
private var hasDeferredScrollOffset: Bool = false
public private(set) var effectiveStoriesInsetHeight: CGFloat = 0.0
override public init(frame: CGRect) {
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
self.separatorLayer = SimpleLayer()
super.init(frame: frame)
self.addSubview(self.backgroundView)
self.layer.addSublayer(self.separatorLayer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.backgroundView.frame.contains(point) {
return nil
}
guard let result = super.hitTest(point, with: event) else {
return nil
}
return result
}
public func applyCurrentScroll(transition: Transition) {
if let rawScrollOffset = self.rawScrollOffset, self.hasDeferredScrollOffset {
self.applyScroll(offset: rawScrollOffset, transition: transition)
}
}
public func applyScroll(offset: CGFloat, transition: Transition) {
self.rawScrollOffset = offset
if self.deferScrollApplication {
self.hasDeferredScrollOffset = true
return
}
guard let component = self.component, let currentLayout = self.currentLayout else {
return
}
let themeUpdated = component.theme !== self.scrollTheme || component.strings !== self.scrollStrings
self.scrollTheme = component.theme
self.scrollStrings = component.strings
let searchOffsetDistance: CGFloat = navigationBarSearchContentHeight
let defaultStoriesOffsetDistance: CGFloat = 94.0
let effectiveStoriesOffsetDistance: CGFloat
var minContentOffset: CGFloat = navigationBarSearchContentHeight
if !component.isSearchActive, let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty {
effectiveStoriesOffsetDistance = defaultStoriesOffsetDistance * (1.0 - component.secondaryTransition)
minContentOffset += effectiveStoriesOffsetDistance
} else {
effectiveStoriesOffsetDistance = 0.0
}
let clippedScrollOffset = min(minContentOffset, offset)
if self.clippedScrollOffset == clippedScrollOffset && !self.hasDeferredScrollOffset {
return
}
self.hasDeferredScrollOffset = false
self.clippedScrollOffset = clippedScrollOffset
let visibleSize = CGSize(width: currentLayout.size.width, height: max(0.0, currentLayout.size.height - clippedScrollOffset))
self.backgroundView.update(size: visibleSize, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: visibleSize))
transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: UIScreenPixel)))
let searchContentNode: NavigationBarSearchContentNode
if let current = self.searchContentNode {
searchContentNode = current
if themeUpdated {
let placeholder: String
let compactPlaceholder: String
placeholder = component.strings.Common_Search
compactPlaceholder = component.strings.Common_Search
searchContentNode.updateThemeAndPlaceholder(theme: component.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder)
}
} else {
let placeholder: String
let compactPlaceholder: String
placeholder = component.strings.Common_Search
compactPlaceholder = component.strings.Common_Search
//TODO:localize
searchContentNode = NavigationBarSearchContentNode(
theme: component.theme,
placeholder: placeholder,
compactPlaceholder: compactPlaceholder,
activate: { [weak self] in
guard let self, let component = self.component, let searchContentNode = self.searchContentNode else {
return
}
component.activateSearch(searchContentNode)
}
)
self.searchContentNode = searchContentNode
self.addSubview(searchContentNode.view)
}
let clippedStoriesOffset = max(0.0, min(clippedScrollOffset, defaultStoriesOffsetDistance))
let storiesOffsetFraction: CGFloat
if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty {
storiesOffsetFraction = clippedStoriesOffset / defaultStoriesOffsetDistance
} else {
storiesOffsetFraction = 1.0
}
let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight)
let searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - 0.0 - searchSize.height), size: searchSize)
let clippedSearchOffset = max(0.0, min(clippedScrollOffset - effectiveStoriesOffsetDistance, searchOffsetDistance))
let searchOffsetFraction = clippedSearchOffset / searchOffsetDistance
searchContentNode.expansionProgress = 1.0 - searchOffsetFraction
transition.setFrame(view: searchContentNode.view, frame: searchFrame)
searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition)
let headerContentSize = self.headerContent.update(
transition: transition,
component: AnyComponent(ChatListHeaderComponent(
sideInset: component.sideInset + 16.0,
primaryContent: component.primaryContent,
secondaryContent: component.secondaryContent,
secondaryTransition: component.secondaryTransition,
networkStatus: nil,
storySubscriptions: component.storySubscriptions,
storiesFraction: 1.0 - storiesOffsetFraction,
context: component.context,
theme: component.theme,
strings: component.strings,
openStatusSetup: { [weak self] sourceView in
guard let self, let component = self.component else {
return
}
component.openStatusSetup(sourceView)
},
toggleIsLocked: { [weak self] in
guard let self, let component = self.component else {
return
}
component.context.sharedContext.appLockContext.lock()
}
)),
environment: {},
containerSize: CGSize(width: currentLayout.size.width, height: 44.0)
)
let headerContentY: CGFloat
if component.isSearchActive {
headerContentY = -headerContentSize.height - effectiveStoriesOffsetDistance
} else {
if component.statusBarHeight < 1.0 {
headerContentY = 0.0
} else {
headerContentY = component.statusBarHeight + 12.0
}
}
let headerContentFrame = CGRect(origin: CGPoint(x: 0.0, y: headerContentY), size: headerContentSize)
if let headerContentView = self.headerContent.view {
if headerContentView.superview == nil {
self.addSubview(headerContentView)
}
transition.setFrame(view: headerContentView, frame: headerContentFrame)
}
}
func update(component: ChatListNavigationBar, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let themeUpdated = self.component?.theme !== component.theme
self.component = component
self.state = state
if themeUpdated {
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.separatorLayer.backgroundColor = component.theme.rootController.navigationBar.separatorColor.cgColor
}
var contentHeight = component.statusBarHeight
if component.statusBarHeight >= 1.0 {
contentHeight += 10.0
}
contentHeight += 44.0
if component.isSearchActive {
if component.statusBarHeight < 1.0 {
contentHeight += 8.0
}
self.effectiveStoriesInsetHeight = 0.0
} else {
if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty {
let storiesHeight: CGFloat = 94.0 * (1.0 - component.secondaryTransition)
contentHeight += storiesHeight
self.effectiveStoriesInsetHeight = storiesHeight
} else {
self.effectiveStoriesInsetHeight = 0.0
}
contentHeight += navigationBarSearchContentHeight
}
let size = CGSize(width: availableSize.width, height: contentHeight)
self.currentLayout = CurrentLayout(size: size)
self.hasDeferredScrollOffset = true
return size
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}