diff --git a/Telegram/BUILD b/Telegram/BUILD index a7e40651fc..aacae96803 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -8,6 +8,8 @@ load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_extension", "ios_framework", + "ios_unit_test", + "ios_ui_test", ) load("@build_bazel_rules_apple//apple:resources.bzl", @@ -20,6 +22,7 @@ load("@build_bazel_rules_swift//swift:swift.bzl", load( "@rules_xcodeproj//xcodeproj:defs.bzl", + "top_level_target", "top_level_targets", "xcodeproj", "xcode_provisioning_profile", diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 06bfd50c46..b8e54aecd3 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -155,8 +155,6 @@ public final class CallListController: TelegramBaseController { if case .navigation = self.mode { self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) } - - self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index 47492d7859..a28100bbf7 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -118,7 +118,6 @@ swift_library( "//submodules/TelegramUI/Components/ButtonComponent", "//submodules/TelegramUI/Components/AnimatedTextComponent", "//submodules/TelegramUI/Components/EdgeEffect", - "//submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 078854b0e6..5c5e8f23e3 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -55,8 +55,6 @@ import TextFormat import AvatarUploadToastScreen import AdsInfoScreen import AdsReportScreen -import SearchBarNode -import ChatListFilterTabContainerNode private final class ContextControllerContentSourceImpl: ContextControllerContentSource { let controller: ViewController @@ -770,8 +768,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) self.updateNavigationMetadata() - - self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) } required public init(coder aDecoder: NSCoder) { @@ -2842,9 +2838,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController func updateHeaderContent() -> (primaryContent: ChatListHeaderComponent.Content?, secondaryContent: ChatListHeaderComponent.Content?) { var primaryContent: ChatListHeaderComponent.Content? if let primaryContext = self.primaryContext { - var displayBackButton: Bool = false - if self.previousItem != nil { - displayBackButton = true + var backTitle: String? + if let previousItem = self.previousItem { + switch previousItem { + case let .item(item): + backTitle = item.title ?? self.presentationData.strings.Common_Back + case .close: + backTitle = self.presentationData.strings.Common_Close + } } var navigationBackTitle: String? if case .chatList(.archive) = self.location { @@ -2857,7 +2858,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatListTitle: primaryContext.chatListTitle, leftButton: primaryContext.leftButton, rightButtons: primaryContext.rightButtons, - backPressed: displayBackButton ? { [weak self] in + backTitle: backTitle, + backPressed: backTitle != nil ? { [weak self] in guard let self else { return } @@ -2874,6 +2876,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatListTitle: secondaryContext.chatListTitle, leftButton: secondaryContext.leftButton, rightButtons: secondaryContext.rightButtons, + backTitle: nil, backPressed: { [weak self] in guard let self else { return @@ -4635,7 +4638,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController searchContentNode = navigationBarView.searchContentNode } - self.activateSearch(filter: filter, query: query, skipScrolling: false, searchContentNode: searchContentNode) + if let searchContentNode { + self.activateSearch(filter: filter, query: query, skipScrolling: false, searchContentNode: searchContentNode) + } } public func activateSearch(query: String? = nil) { @@ -4649,37 +4654,45 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } private var previousSearchToggleTimestamp: Double? - func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil, skipScrolling: Bool = false, searchContentNode: NavigationBarSearchContentNode?) { - Task { @MainActor [weak self] in - guard let self else { + func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil, skipScrolling: Bool = false, searchContentNode: NavigationBarSearchContentNode) { + let currentTimestamp = CACurrentMediaTime() + if let previousSearchActivationTimestamp = self.previousSearchToggleTimestamp, currentTimestamp < previousSearchActivationTimestamp + 0.6 { + return + } + self.previousSearchToggleTimestamp = currentTimestamp + + if let storyTooltip = self.storyTooltip { + storyTooltip.dismiss() + } + + var filter = filter + if case .forum = self.chatListDisplayNode.effectiveContainerNode.location { + filter = .topics + } + + if self.chatListDisplayNode.searchDisplayController == nil { + /*if !skipScrolling, let searchContentNode = self.searchContentNode, searchContentNode.expansionProgress != 1.0 { + self.scrollToTop?() + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in + self?.activateSearch(filter: filter, query: query, skipScrolling: true) + }) return - } - - let currentTimestamp = CACurrentMediaTime() - if let previousSearchActivationTimestamp = self.previousSearchToggleTimestamp, currentTimestamp < previousSearchActivationTimestamp + 0.6 { - return - } - self.previousSearchToggleTimestamp = currentTimestamp + }*/ + //TODO:scroll to top? - if let storyTooltip = self.storyTooltip { - storyTooltip.dismiss() - } - - var filter = filter - if case .forum = self.chatListDisplayNode.effectiveContainerNode.location { - filter = .topics - } - - if self.chatListDisplayNode.searchDisplayController == nil { - let (_, _) = await combineLatest(self.chatListDisplayNode.mainContainerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1)).get() - - do { + let _ = (combineLatest(self.chatListDisplayNode.mainContainerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] _, chatListView in + Task { @MainActor in + guard let strongSelf = self else { + return + } + /*if let scrollToTop = strongSelf.scrollToTop { - scrollToTop() - }*/ + scrollToTop() + }*/ let tabsIsEmpty: Bool - if let (resolvedItems, displayTabsAtBottom, _) = self.tabContainerData { + if let (resolvedItems, displayTabsAtBottom, _) = strongSelf.tabContainerData { tabsIsEmpty = resolvedItems.count <= 1 || displayTabsAtBottom } else { tabsIsEmpty = true @@ -4689,43 +4702,40 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let displaySearchFilters = true - if let filterContainerNodeAndActivate = await self.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode?.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: self.hasDownloads, initialFilter: filter, navigationController: self.navigationController as? NavigationController, searchBarIsExternal: true) { + if let filterContainerNodeAndActivate = await strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: strongSelf.hasDownloads, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) { let (filterContainerNode, activate) = filterContainerNodeAndActivate if displaySearchFilters { let searchTabsNode = SparseNode() - self.searchTabsNode = searchTabsNode + strongSelf.searchTabsNode = searchTabsNode searchTabsNode.addSubnode(filterContainerNode) } activate(filter != .downloads) - if let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { + if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { searchContentNode.search(filter: filter, query: query) } } let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) - self.setDisplayNavigationBar(false, transition: transition) - self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: true), transition: transition) - if let searchBarNode = self.currentTabBarSearchNode?() as? SearchBarNode { - self.chatListDisplayNode.searchDisplayController?.setSearchBar(searchBarNode) - searchBarNode.activate() - } - - self.isSearchActive = true - if let navigationController = self.navigationController as? NavigationController { - for controller in navigationController.globalOverlayControllers { - if let controller = controller as? VoiceChatOverlayController { - controller.updateVisibility() - break - } - } + strongSelf.setDisplayNavigationBar(false, transition: transition) + + (strongSelf.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.4, curve: .spring)) + } + }) + + self.isSearchActive = true + if let navigationController = self.navigationController as? NavigationController { + for controller in navigationController.globalOverlayControllers { + if let controller = controller as? VoiceChatOverlayController { + controller.updateVisibility() + break } } - } else if self.isSearchActive { - if let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { - searchContentNode.search(filter: filter, query: query) - } + } + } else if self.isSearchActive { + if let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { + searchContentNode.search(filter: filter, query: query) } } } @@ -4756,8 +4766,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } completion = self.chatListDisplayNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) searchContentNode.placeholderNode.frame = previousFrame - } else { - completion = self.chatListDisplayNode.deactivateSearch(placeholderNode: nil, animated: animated) } self.chatListDisplayNode.tempAllowAvatarExpansion = true @@ -4772,7 +4780,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController completion?() - self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: transition) + (self.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.4, curve: .spring)) self.isSearchActive = false if let navigationController = self.navigationController as? NavigationController { @@ -6228,14 +6236,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) }) } - - override public func tabBarActivateSearch() { - self.activateSearch() - } - - override public func tabBarDeactivateSearch() { - self.deactivateSearch(animated: true) - } private var playedSignUpCompletedAnimation = false public func playSignUpCompletedAnimation() { diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 9b4a72354e..3a8357317e 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -22,7 +22,6 @@ import ChatListHeaderComponent import StoryPeerListComponent import TelegramNotices import EdgeEffect -import ChatListFilterTabContainerNode public enum ChatListContainerNodeFilter: Equatable { case all @@ -1383,8 +1382,8 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { strings: self.presentationData.strings, statusBarHeight: layout.statusBarHeight ?? 0.0, sideInset: layout.safeInsets.left, - search: nil, //ChatListNavigationBar.Search(isEnabled: true), isSearchActive: self.isSearchDisplayControllerActive, + isSearchEnabled: true, primaryContent: headerContent?.primaryContent, secondaryContent: headerContent?.secondaryContent, secondaryTransition: self.inlineStackContainerTransitionFraction, @@ -1541,8 +1540,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { var storiesInset = storiesInset let navigationBarLayout = self.updateNavigationBar(layout: layout, deferScrollApplication: true, transition: ComponentTransition(transition)) - //self.mainContainerNode.initialScrollingOffset = ChatListNavigationBar.searchScrollHeight + navigationBarLayout.storiesInset - self.mainContainerNode.initialScrollingOffset = navigationBarLayout.storiesInset + self.mainContainerNode.initialScrollingOffset = ChatListNavigationBar.searchScrollHeight + navigationBarLayout.storiesInset navigationBarHeight = navigationBarLayout.navigationHeight visualNavigationHeight = navigationBarLayout.navigationHeight @@ -1668,7 +1666,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } @MainActor - func activateSearch(placeholderNode: SearchBarPlaceholderNode?, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?, searchBarIsExternal: Bool) async -> (ASDisplayNode, (Bool) -> Void)? { + func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) async -> (ASDisplayNode, (Bool) -> Void)? { guard let (containerLayout, _, _, cleanNavigationBarHeight, _) = self.containerLayout, self.searchDisplayController == nil else { return nil } @@ -1714,7 +1712,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }, searchBarIsExternal: searchBarIsExternal) + }) self.mainContainerNode.accessibilityElementsHidden = true self.inlineStackContainerNode?.accessibilityElementsHidden = true @@ -1744,7 +1742,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { }) } - func deactivateSearch(placeholderNode: SearchBarPlaceholderNode?, animated: Bool) -> (() -> Void)? { + func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) -> (() -> Void)? { if let searchDisplayController = self.searchDisplayController { self.isSearchDisplayControllerActive = false self.searchDisplayController = nil @@ -1752,8 +1750,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { self.inlineStackContainerNode?.accessibilityElementsHidden = false return { [weak self, weak placeholderNode] in - if let strongSelf = self, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout { - let placeholderNode = placeholderNode + if let strongSelf = self, let placeholderNode, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout { searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) diff --git a/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift similarity index 96% rename from submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/Sources/ChatListFilterTabContainerNode.swift rename to submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index 9e3fb4dd03..b20fc85916 100644 --- a/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -210,7 +210,7 @@ private final class ItemNode: ASDisplayNode { if self.theme !== presentationData.theme { self.theme = presentationData.theme - self.badgeBackgroundActiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chat.inputPanel.panelControlColor) + self.badgeBackgroundActiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chatList.unreadBadgeActiveBackgroundColor) self.badgeBackgroundInactiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chatList.unreadBadgeInactiveBackgroundColor) themeUpdated = true @@ -299,10 +299,10 @@ private final class ItemNode: ASDisplayNode { if themeUpdated || titleUpdated { self.titleNode.attributedText = title.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.list.itemSecondaryTextColor) - self.titleActiveNode.attributedText = title.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor) + self.titleActiveNode.attributedText = title.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.list.itemAccentColor) self.shortTitleNode.attributedText = shortTitle.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.list.itemSecondaryTextColor) - self.shortTitleActiveNode.attributedText = shortTitle.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor) + self.shortTitleActiveNode.attributedText = shortTitle.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.list.itemAccentColor) } if unreadCount != 0 { @@ -438,8 +438,8 @@ public enum ChatListFilterTabEntryId: Hashable { } public struct ChatListFilterTabEntryUnreadCount: Equatable { - public let value: Int - public let hasUnmuted: Bool + let value: Int + let hasUnmuted: Bool public init(value: Int, hasUnmuted: Bool) { self.value = value @@ -482,14 +482,14 @@ public enum ChatListFilterTabEntry: Equatable { public final class ChatListFilterTabContainerNode: ASDisplayNode { private let context: AccountContext private let scrollNode: ASScrollNode - private let selectedBackgroundNode: ASImageNode + private let selectedLineNode: ASImageNode private var itemNodes: [ChatListFilterTabEntryId: ItemNode] = [:] public var tabSelected: ((ChatListFilterTabEntryId, Bool) -> Void)? - public var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)? - public var addFilter: (() -> Void)? - public var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)? - public var presentPremiumTip: (() -> Void)? + var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)? + var addFilter: (() -> Void)? + var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)? + var presentPremiumTip: (() -> Void)? private var reorderingGesture: ReorderingGestureRecognizer? private var reorderingItem: ChatListFilterTabEntryId? @@ -501,7 +501,7 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { private var currentParams: (size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, isReordering: Bool, isEditing: Bool, canReorderAllChats: Bool, filtersLimit: Int32?, transitionFraction: CGFloat, presentationData: PresentationData)? - public var reorderedFilterIds: [Int32]? { + var reorderedFilterIds: [Int32]? { return self.reorderedItemIds.flatMap { $0.compactMap { switch $0 { @@ -533,9 +533,9 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { self.context = context self.scrollNode = ASScrollNode() - self.selectedBackgroundNode = ASImageNode() - self.selectedBackgroundNode.displaysAsynchronously = false - self.selectedBackgroundNode.displayWithoutProcessing = true + self.selectedLineNode = ASImageNode() + self.selectedLineNode.displaysAsynchronously = false + self.selectedLineNode.displayWithoutProcessing = true super.init() @@ -549,7 +549,7 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { } self.addSubnode(self.scrollNode) - self.scrollNode.addSubnode(self.selectedBackgroundNode) + self.scrollNode.addSubnode(self.selectedLineNode) let reorderingGesture = ReorderingGestureRecognizer(shouldBegin: { [weak self] point in guard let strongSelf = self else { @@ -677,7 +677,7 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { private var previousSelectedFrame: CGRect? public func cancelAnimations() { - self.selectedBackgroundNode.layer.removeAllAnimations() + self.selectedLineNode.layer.removeAllAnimations() self.scrollNode.layer.removeAllAnimations() } @@ -695,7 +695,14 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { let previousContentWidth = self.scrollNode.view.contentSize.width if self.currentParams?.presentationData.theme !== presentationData.theme { - self.selectedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 36.0, color: presentationData.theme.chatList.itemHighlightedBackgroundColor) + self.selectedLineNode.image = generateImage(CGSize(width: 5.0, height: 3.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: 4.0, height: 4.0))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - 4.0, y: 0.0), size: CGSize(width: 4.0, height: 4.0))) + context.fill(CGRect(x: 2.0, y: 0.0, width: size.width - 4.0, height: 4.0)) + context.fill(CGRect(x: 0.0, y: 2.0, width: size.width, height: 2.0)) + })?.resizableImage(withCapInsets: UIEdgeInsets(top: 3.0, left: 3.0, bottom: 0.0, right: 3.0), resizingMode: .stretch) } if isReordering { @@ -857,7 +864,7 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { let minSpacing: CGFloat = 26.0 - let resolvedSideInset: CGFloat = 25.0 + sideInset + let resolvedSideInset: CGFloat = 16.0 + sideInset var leftOffset: CGFloat = resolvedSideInset var longTitlesWidth: CGFloat = resolvedSideInset @@ -930,13 +937,13 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { } if let selectedFrame = selectedFrame { - let wasAdded = self.selectedBackgroundNode.isHidden - self.selectedBackgroundNode.isHidden = false - let selectedBackgroundFrame = CGRect(origin: CGPoint(x: selectedFrame.minX - 10.0, y: selectedFrame.minY - floor((36.0 - selectedFrame.height) * 0.5)), size: CGSize(width: selectedFrame.width + 10.0 * 2.0, height: 36.0)) + let wasAdded = self.selectedLineNode.isHidden + self.selectedLineNode.isHidden = false + let lineFrame = CGRect(origin: CGPoint(x: selectedFrame.minX, y: size.height - 3.0), size: CGSize(width: selectedFrame.width, height: 3.0)) if wasAdded { - self.selectedBackgroundNode.frame = selectedBackgroundFrame + self.selectedLineNode.frame = lineFrame } else { - transition.updateFrame(node: self.selectedBackgroundNode, frame: selectedBackgroundFrame) + transition.updateFrame(node: self.selectedLineNode, frame: lineFrame) } if let previousSelectedFrame = self.previousSelectedFrame { @@ -965,7 +972,7 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { self.previousSelectedAbsFrame = selectedFrame.offsetBy(dx: -self.scrollNode.bounds.minX, dy: 0.0) self.previousSelectedFrame = selectedFrame } else { - self.selectedBackgroundNode.isHidden = true + self.selectedLineNode.isHidden = true self.previousSelectedAbsFrame = nil self.previousSelectedFrame = nil } diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 9b445ccc3f..46974fd940 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -2103,9 +2103,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let effectiveBackgroundColor: UIColor if item.isPinned { onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned, voiceChat: self.onlineIsVoiceChat) - //TODO:localize - effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor - //effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor + effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor } else { onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: self.onlineIsVoiceChat) effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor @@ -2119,9 +2117,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { if let avatarLiveBadge = self.avatarLiveBadge { let effectiveBackgroundColor: UIColor if item.isPinned { - //TODO:localize - effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor - //effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor + effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor } else { effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor } @@ -4131,9 +4127,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let effectiveBackgroundColor: UIColor if item.isPinned { - //TODO:localize - effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor - //effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor + effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor } else { effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor } @@ -4154,9 +4148,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { effectiveBackgroundColor = item.presentationData.theme.chatList.itemHighlightedBackgroundColor } else if case let .chatList(index) = item.index, index.pinningIndex != nil { onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned, voiceChat: onlineIsVoiceChat) - //TODO:localize - effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor - //effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor + effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor } else { onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat) effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor @@ -5068,9 +5060,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { backgroundColor = theme.itemBackgroundColor highlightedBackgroundColor = theme.itemHighlightedBackgroundColor } else { - //TODO:localize - backgroundColor = item.presentationData.theme.chatList.itemBackgroundColor - //backgroundColor = theme.pinnedItemBackgroundColor + backgroundColor = theme.pinnedItemBackgroundColor highlightedBackgroundColor = theme.pinnedItemHighlightedBackgroundColor } } else { diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift index ae393778c4..4dcd0401d2 100644 --- a/submodules/ComponentFlow/Source/Base/Transition.swift +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -1335,41 +1335,6 @@ public struct ComponentTransition { completion: completion ) } - - public func setBlur(layer: CALayer, radius: CGFloat, completion: ((Bool) -> Void)? = nil) { - var currentRadius: CGFloat = 0.0 - if let currentFilters = layer.filters { - for filter in currentFilters { - if let filter = filter as? NSObject, filter.description.contains("gaussianBlur") { - currentRadius = filter.value(forKey: "inputRadius") as? CGFloat ?? 0.0 - } - } - } - - if currentRadius == radius { - completion?(true) - return - } - - if let blurFilter = CALayer.blur() { - blurFilter.setValue(radius as NSNumber, forKey: "inputRadius") - layer.filters = [blurFilter] - switch self.animation { - case .none: - completion?(true) - case let .curve(duration, curve): - layer.animate(from: currentRadius as NSNumber, to: radius as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", duration: duration, delay: 0.0, curve: curve, removeOnCompletion: true, additive: false,completion: { [weak layer] flag in - if let layer { - if radius <= 0.0 { - layer.filters = nil - } - } - - completion?(flag) - }) - } - } - } public func animateBlur(layer: CALayer, fromRadius: CGFloat, toRadius: CGFloat, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) { let duration: Double diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index e75897f583..425ca2b7ab 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -226,8 +226,6 @@ public class ContactsController: ViewController { } self.sortButton.addTarget(self, action: #selector(self.sortPressed), forControlEvents: .touchUpInside) - - self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index bd1e967a3b..2718f81f91 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -350,6 +350,7 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { chatListTitle: NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil), leftButton: leftButton, rightButtons: rightButtons, + backTitle: nil, backPressed: nil ) @@ -361,8 +362,8 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { strings: self.presentationData.strings, statusBarHeight: layout.statusBarHeight ?? 0.0, sideInset: layout.safeInsets.left, - search: ChatListNavigationBar.Search(isEnabled: true), isSearchActive: self.isSearchDisplayControllerActive, + isSearchEnabled: true, primaryContent: primaryContent, secondaryContent: nil, secondaryTransition: 0.0, diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index f81403f2dd..593005a7e5 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -217,18 +217,6 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { open var navigationBarRequiresEntireLayoutUpdate: Bool { return true } - - public struct TabBarSearchState: Equatable { - public var isActive: Bool - - public init(isActive: Bool) { - self.isActive = isActive - } - } - - public private(set) var tabBarSearchState: TabBarSearchState? - public var tabBarSearchStateUpdated: ((ContainedViewLayoutTransition) -> Void)? - public var currentTabBarSearchNode: (() -> ASDisplayNode?)? private weak var activeInputViewCandidate: UIResponder? private weak var activeInputView: UIResponder? @@ -717,22 +705,9 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { open func tabBarDisabledAction() { } - - open func tabBarActivateSearch() { - } - - open func tabBarDeactivateSearch() { - } open func tabBarItemSwipeAction(direction: TabBarItemSwipeDirection) { } - - public func updateTabBarSearchState(_ tabBarSearchState: TabBarSearchState?, transition: ContainedViewLayoutTransition) { - if self.tabBarSearchState != tabBarSearchState { - self.tabBarSearchState = tabBarSearchState - self.tabBarSearchStateUpdated?(transition) - } - } open func updatePossibleControllerDropContent(content: NavigationControllerDropContent?) { } diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 773e1f4259..131323d318 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -369,7 +369,6 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { } var theme: SearchBarNodeTheme - let style: SearchBarStyle fileprivate func layoutTokens(transition: ContainedViewLayoutTransition = .immediate) { var hasSelected = false @@ -520,9 +519,8 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { } } - init(theme: SearchBarNodeTheme, style: SearchBarStyle) { + init(theme: SearchBarNodeTheme) { self.theme = theme - self.style = style self.placeholderLabel = ImmediateTextNode() self.placeholderLabel.isUserInteractionEnabled = false @@ -681,10 +679,6 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { placeholderYOffset = 1.0 } else { } - - if case .inlineNavigation = self.style { - placeholderYOffset += 6.0 - } let textRect = self.textRect(forBounds: bounds) let labelSize = self.placeholderLabel.updateLayout(textRect.size) @@ -819,45 +813,40 @@ public final class SearchBarNodeTheme: Equatable { public enum SearchBarStyle { case modern case legacy - case inlineNavigation var font: UIFont { switch self { - case .modern, .inlineNavigation: - return Font.regular(17.0) - case .legacy: - return Font.regular(14.0) + case .modern: + return Font.regular(17.0) + case .legacy: + return Font.regular(14.0) } } var cornerDiameter: CGFloat { switch self { - case .modern, .inlineNavigation: - return 21.0 - case .legacy: - return 14.0 + case .modern: + return 21.0 + case .legacy: + return 14.0 } } var height: CGFloat { switch self { - case .inlineNavigation: - return 48.0 - case .modern: - return 36.0 - case .legacy: - return 28.0 + case .modern: + return 36.0 + case .legacy: + return 28.0 } } var padding: CGFloat { switch self { - case .inlineNavigation: - return 0.0 - case .modern: - return 10.0 - case .legacy: - return 8.0 + case .modern: + return 10.0 + case .legacy: + return 8.0 } } } @@ -1005,7 +994,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true - self.textField = SearchBarTextField(theme: theme, style: fieldStyle) + self.textField = SearchBarTextField(theme: theme) self.textField.accessibilityTraits = .searchField self.textField.autocorrectionType = .no self.textField.returnKeyType = .search @@ -1022,21 +1011,14 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { super.init() - if case .inlineNavigation = self.fieldStyle { - } else { - self.addSubnode(self.backgroundNode) - self.addSubnode(self.separatorNode) - self.addSubnode(self.textBackgroundNode) - } + self.addSubnode(self.backgroundNode) + self.addSubnode(self.separatorNode) + + self.addSubnode(self.textBackgroundNode) self.view.addSubview(self.textField) - - if case .inlineNavigation = self.fieldStyle { - } else { - self.addSubnode(self.iconNode) - self.addSubnode(self.cancelButton) - } - + self.addSubnode(self.iconNode) self.addSubnode(self.clearButton) + self.addSubnode(self.cancelButton) self.textField.delegate = self self.textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged) @@ -1111,34 +1093,19 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: .immediate) transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: self.bounds.size.height), size: CGSize(width: self.bounds.size.width, height: UIScreenPixel))) + let verticalOffset: CGFloat = boundingSize.height - 82.0 + let contentFrame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: boundingSize.width - leftInset - rightInset, height: boundingSize.height)) - let textBackgroundHeight: CGFloat - if case .inlineNavigation = self.fieldStyle { - textBackgroundHeight = boundingSize.height - } else { - textBackgroundHeight = self.fieldStyle.height - } - let verticalOffset: CGFloat - if case .inlineNavigation = self.fieldStyle { - verticalOffset = -textBackgroundHeight - } else { - verticalOffset = boundingSize.height - 82.0 - } + let textBackgroundHeight = self.fieldStyle.height let cancelButtonSize = self.cancelButton.measure(CGSize(width: 100.0, height: CGFloat.infinity)) transition.updateFrame(node: self.cancelButton, frame: CGRect(origin: CGPoint(x: contentFrame.maxX - 10.0 - cancelButtonSize.width, y: verticalOffset + textBackgroundHeight + floorToScreenPixels((textBackgroundHeight - cancelButtonSize.height) / 2.0)), size: cancelButtonSize)) let padding = self.fieldStyle.padding - var textBackgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX + padding, y: verticalOffset + textBackgroundHeight), size: CGSize(width: contentFrame.width - padding * 2.0 - (self.hasCancelButton ? cancelButtonSize.width + 11.0 : 0.0), height: textBackgroundHeight)) + let textBackgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX + padding, y: verticalOffset + textBackgroundHeight), size: CGSize(width: contentFrame.width - padding * 2.0 - (self.hasCancelButton ? cancelButtonSize.width + 11.0 : 0.0), height: textBackgroundHeight)) transition.updateFrame(node: self.textBackgroundNode, frame: textBackgroundFrame) - var textFrame = CGRect(origin: CGPoint(x: 0.0, y: textBackgroundFrame.minY), size: CGSize(width: max(1.0, textBackgroundFrame.size.width - 24.0 - 27.0), height: textBackgroundFrame.size.height)) - if case .inlineNavigation = self.fieldStyle { - textFrame.size.width = boundingSize.width - 27.0 - textBackgroundFrame.size.width = boundingSize.width - } else { - textFrame.origin.x = textBackgroundFrame.minX + 24.0 - } + let textFrame = CGRect(origin: CGPoint(x: textBackgroundFrame.minX + 24.0, y: textBackgroundFrame.minY), size: CGSize(width: max(1.0, textBackgroundFrame.size.width - 24.0 - 27.0), height: textBackgroundFrame.size.height)) if let iconImage = self.iconNode.image { let iconSize = iconImage.size diff --git a/submodules/SearchUI/Sources/SearchDisplayController.swift b/submodules/SearchUI/Sources/SearchDisplayController.swift index 3c885e33e0..50420e6b50 100644 --- a/submodules/SearchUI/Sources/SearchDisplayController.swift +++ b/submodules/SearchUI/Sources/SearchDisplayController.swift @@ -25,14 +25,12 @@ public final class SearchDisplayController { } } - private var searchBar: SearchBarNode? - private let searchBarIsExternal: Bool + private let searchBar: SearchBarNode private let mode: SearchDisplayControllerMode private let backgroundNode: BackgroundNode public let contentNode: SearchDisplayControllerContentNode private var hasSeparator: Bool private let inline: Bool - private let cancel: () -> Void private var containerLayout: (ContainerViewLayout, CGFloat)? @@ -40,75 +38,71 @@ public final class SearchDisplayController { private var isSearchingDisposable: Disposable? - public init( - presentationData: PresentationData, - mode: SearchDisplayControllerMode = .navigation, - placeholder: String? = nil, - hasBackground: Bool = false, - hasSeparator: Bool = false, - contentNode: SearchDisplayControllerContentNode, - inline: Bool = false, - cancel: @escaping () -> Void, - fieldStyle: SearchBarStyle = .modern, - searchBarIsExternal: Bool = false - ) { + public init(presentationData: PresentationData, mode: SearchDisplayControllerMode = .navigation, placeholder: String? = nil, hasBackground: Bool = false, hasSeparator: Bool = false, contentNode: SearchDisplayControllerContentNode, inline: Bool = false, cancel: @escaping () -> Void) { self.inline = inline - self.cancel = cancel - self.searchBarIsExternal = searchBarIsExternal - - if !searchBarIsExternal { - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasBackground: hasBackground, hasSeparator: hasSeparator, inline: inline), strings: presentationData.strings, fieldStyle: fieldStyle, forceSeparator: hasSeparator, displayBackground: hasBackground) - } + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasBackground: hasBackground, hasSeparator: hasSeparator, inline: inline), strings: presentationData.strings, fieldStyle: .modern, forceSeparator: hasSeparator, displayBackground: hasBackground) self.backgroundNode = BackgroundNode() self.backgroundNode.allowsGroupOpacity = true self.mode = mode self.contentNode = contentNode self.hasSeparator = hasSeparator - - if let searchBar = self.searchBar { - self.setSearchBar(searchBar) - } + self.searchBar.textUpdated = { [weak contentNode] text, _ in + contentNode?.searchTextUpdated(text: text) + } + self.searchBar.tokensUpdated = { [weak contentNode] tokens in + contentNode?.searchTokensUpdated(tokens: tokens) + } + self.searchBar.cancel = { [weak self] in + self?.isDeactivating = true + cancel() + } + self.searchBar.clearPrefix = { [weak contentNode] in + contentNode?.searchTextClearPrefix() + } + self.searchBar.clearTokens = { [weak contentNode] in + contentNode?.searchTextClearTokens() + } self.contentNode.cancel = { [weak self] in self?.isDeactivating = true cancel() } self.contentNode.dismissInput = { [weak self] in - self?.searchBar?.deactivate(clear: false) + self?.searchBar.deactivate(clear: false) } var isFirstTime = true self.contentNode.setQuery = { [weak self] prefix, tokens, query in - if let strongSelf = self, let searchBar = strongSelf.searchBar { - searchBar.prefixString = prefix - let previousTokens = searchBar.tokens - searchBar.tokens = tokens - searchBar.text = query + if let strongSelf = self { + strongSelf.searchBar.prefixString = prefix + let previousTokens = strongSelf.searchBar.tokens + strongSelf.searchBar.tokens = tokens + strongSelf.searchBar.text = query if previousTokens.count < tokens.count && !isFirstTime { if let lastToken = tokens.last, !lastToken.permanent { - searchBar.selectLastToken() + strongSelf.searchBar.selectLastToken() } } isFirstTime = false } } if let placeholder = placeholder { - self.searchBar?.placeholderString = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationSearchBar.inputPlaceholderTextColor) + self.searchBar.placeholderString = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationSearchBar.inputPlaceholderTextColor) } self.contentNode.setPlaceholder = { [weak self] string in - guard string != self?.searchBar?.placeholderString?.string else { + guard string != self?.searchBar.placeholderString?.string else { return } - if let mutableAttributedString = self?.searchBar?.placeholderString?.mutableCopy() as? NSMutableAttributedString { + if let mutableAttributedString = self?.searchBar.placeholderString?.mutableCopy() as? NSMutableAttributedString { mutableAttributedString.mutableString.setString(string) - self?.searchBar?.placeholderString = mutableAttributedString + self?.searchBar.placeholderString = mutableAttributedString } } self.isSearchingDisposable = (contentNode.isSearching |> deliverOnMainQueue).start(next: { [weak self] value in - self?.searchBar?.activity = value + self?.searchBar.activity = value }) if self.contentNode.hasDim { @@ -119,32 +113,9 @@ public final class SearchDisplayController { self.backgroundNode.isTransparent = false } } - - public func setSearchBar(_ searchBar: SearchBarNode) { - self.searchBar = searchBar - - searchBar.textUpdated = { [weak contentNode] text, _ in - contentNode?.searchTextUpdated(text: text) - } - searchBar.tokensUpdated = { [weak contentNode] tokens in - contentNode?.searchTokensUpdated(tokens: tokens) - } - searchBar.cancel = { [weak self] in - self?.isDeactivating = true - self?.cancel() - } - searchBar.clearPrefix = { [weak contentNode] in - contentNode?.searchTextClearPrefix() - } - searchBar.clearTokens = { [weak contentNode] in - contentNode?.searchTextClearTokens() - } - } public func updatePresentationData(_ presentationData: PresentationData) { - if !self.searchBarIsExternal { - self.searchBar?.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: self.hasSeparator, inline: self.inline), strings: presentationData.strings) - } + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: self.hasSeparator, inline: self.inline), strings: presentationData.strings) self.contentNode.updatePresentationData(presentationData) if self.contentNode.hasDim { @@ -166,27 +137,21 @@ public final class SearchDisplayController { navigationBarOffset = 0.0 } var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationBarOffset), size: CGSize(width: layout.size.width, height: searchBarHeight)) - if self.searchBarIsExternal { - navigationBarFrame.size.height -= 10.0 - } if layout.statusBarHeight == nil { navigationBarFrame.size.height = 64.0 } navigationBarFrame.size.height += 10.0 - let navigationBarHeight = navigationBarFrame.maxY - if !self.searchBarIsExternal, let searchBar = self.searchBar { - let searchBarFrame: CGRect - if case .navigation = self.mode { - searchBarFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 54.0) - } else { - searchBarFrame = navigationBarFrame - } - transition.updateFrame(node: searchBar, frame: searchBarFrame) - searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) + let searchBarFrame: CGRect + if case .navigation = self.mode { + searchBarFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 54.0) + } else { + searchBarFrame = navigationBarFrame } + transition.updateFrame(node: self.searchBar, frame: searchBarFrame) + self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) - self.containerLayout = (layout, navigationBarHeight) + self.containerLayout = (layout, navigationBarFrame.maxY) let bounds = CGRect(origin: CGPoint(), size: layout.size) transition.updateFrame(node: self.backgroundNode, frame: bounds.insetBy(dx: -20.0, dy: -20.0)) @@ -240,20 +205,18 @@ public final class SearchDisplayController { self.backgroundNode.layer.animateScale(from: 0.85, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) } - if !self.searchBarIsExternal { - if !self.contentNode.hasDim { - if let placeholder = placeholder { - self.searchBar?.placeholderString = placeholder.placeholderString - } - } else { - if let placeholder = placeholder { - let initialTextBackgroundFrame = placeholder.convert(placeholder.backgroundNode.frame, to: nil) - let contentNodePosition = self.backgroundNode.layer.position - if contentNode.animateBackgroundAppearance { - self.backgroundNode.layer.animatePosition(from: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (initialTextBackgroundFrame.maxY + 8.0 - contentNavigationBarHeight)), to: contentNodePosition, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) - } - self.searchBar?.placeholderString = placeholder.placeholderString + if !self.contentNode.hasDim { + if let placeholder = placeholder { + self.searchBar.placeholderString = placeholder.placeholderString + } + } else { + if let placeholder = placeholder { + let initialTextBackgroundFrame = placeholder.convert(placeholder.backgroundNode.frame, to: nil) + let contentNodePosition = self.backgroundNode.layer.position + if contentNode.animateBackgroundAppearance { + self.backgroundNode.layer.animatePosition(from: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (initialTextBackgroundFrame.maxY + 8.0 - contentNavigationBarHeight)), to: contentNodePosition, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) } + self.searchBar.placeholderString = placeholder.placeholderString } } @@ -277,49 +240,39 @@ public final class SearchDisplayController { navigationBarFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 54.0) } - if !self.searchBarIsExternal, let searchBar = self.searchBar { - searchBar.frame = navigationBarFrame - insertSubnode(searchBar, true) - searchBar.layout() - - if focus { - searchBar.activate() - } + self.searchBar.frame = navigationBarFrame + insertSubnode(self.searchBar, true) + self.searchBar.layout() + + if focus { + self.searchBar.activate() } - if let placeholder = placeholder { - if !self.searchBarIsExternal, let searchBar = self.searchBar { - searchBar.animateIn(from: placeholder, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) - } + self.searchBar.animateIn(from: placeholder, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) if self.contentNode.hasDim { self.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } else { - if !self.searchBarIsExternal, let searchBar = self.searchBar { - searchBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) - } + self.searchBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) self.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } public func deactivate(placeholder: SearchBarPlaceholderNode?, animated: Bool = true) { - if let searchBar = self.searchBar { - searchBar.deactivate(clear: false) - } + self.searchBar.deactivate(clear: false) - if !self.searchBarIsExternal, let searchBar = self.searchBar { - if let placeholder = placeholder { - searchBar.transitionOut(to: placeholder, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate, completion: { - [weak searchBar] in + let searchBar = self.searchBar + if let placeholder = placeholder { + searchBar.transitionOut(to: placeholder, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate, completion: { + [weak searchBar] in + searchBar?.removeFromSupernode() + }) + } else { + searchBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak searchBar] finished in + if finished { searchBar?.removeFromSupernode() - }) - } else { - searchBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak searchBar] finished in - if finished { - searchBar?.removeFromSupernode() - } - }) - } + } + }) } let backgroundNode = self.backgroundNode diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index c346769805..abee862105 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -18,18 +18,15 @@ final class TabBarControllerNode: ASDisplayNode { let layout: ContainerViewLayout let toolbar: Toolbar? let isTabBarHidden: Bool - let currentControllerSearchState: ViewController.TabBarSearchState? init( layout: ContainerViewLayout, toolbar: Toolbar?, - isTabBarHidden: Bool, - currentControllerSearchState: ViewController.TabBarSearchState? + isTabBarHidden: Bool ) { self.layout = layout self.toolbar = toolbar self.isTabBarHidden = isTabBarHidden - self.currentControllerSearchState = currentControllerSearchState } } @@ -54,7 +51,6 @@ final class TabBarControllerNode: ASDisplayNode { } private var theme: PresentationTheme - private var strings: PresentationStrings private let itemSelected: (Int, Bool, [ASDisplayNode]) -> Void private let contextAction: (Int, ContextExtractedContentContainingView, ContextGesture) -> Void @@ -64,26 +60,24 @@ final class TabBarControllerNode: ASDisplayNode { private var toolbarNode: ToolbarNode? private let toolbarActionSelected: (ToolbarActionOption) -> Void private let disabledPressed: () -> Void - private let activateSearch: () -> Void - private let deactivateSearch: () -> Void private(set) var tabBarItems: [TabBarNodeItem] = [] private(set) var selectedIndex: Int = 0 - private weak var currentController: ViewController? + private(set) var currentControllerNode: ASDisplayNode? private var layoutResult: LayoutResult? private var isUpdateRequested: Bool = false private var isChangingSelectedIndex: Bool = false - func setCurrentController(_ controller: ViewController?) -> () -> Void { - guard controller !== self.currentController else { + func setCurrentControllerNode(_ node: ASDisplayNode?) -> () -> Void { + guard node !== self.currentControllerNode else { return {} } - let previousNode = self.currentController?.displayNode - self.currentController = controller - if let currentControllerNode = self.currentController?.displayNode { + let previousNode = self.currentControllerNode + self.currentControllerNode = node + if let currentControllerNode = self.currentControllerNode { if let previousNode { self.insertSubnode(currentControllerNode, aboveSubnode: previousNode) } else { @@ -95,22 +89,14 @@ final class TabBarControllerNode: ASDisplayNode { } return { [weak self, weak previousNode] in - if previousNode !== self?.currentController?.displayNode { + if previousNode !== self?.currentControllerNode { previousNode?.removeFromSupernode() } } } - - var currentSearchNode: ASDisplayNode? { - if let tabBarComponentView = self.tabBarView.view as? TabBarComponent.View { - return tabBarComponentView.currentSearchNode - } - return nil - } - init(theme: PresentationTheme, strings: PresentationStrings, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingView, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void, activateSearch: @escaping () -> Void, deactivateSearch: @escaping () -> Void) { + init(theme: PresentationTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingView, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) { self.theme = theme - self.strings = strings self.itemSelected = itemSelected self.contextAction = contextAction self.disabledOverlayNode = ASDisplayNode() @@ -118,9 +104,7 @@ final class TabBarControllerNode: ASDisplayNode { self.disabledOverlayNode.alpha = 0.0 self.toolbarActionSelected = toolbarActionSelected self.disabledPressed = disabledPressed - self.activateSearch = activateSearch - self.deactivateSearch = deactivateSearch - + super.init() self.setViewBlock({ @@ -179,7 +163,7 @@ final class TabBarControllerNode: ASDisplayNode { } func containerLayoutUpdated(_ layout: ContainerViewLayout, toolbar: Toolbar?, transition: ContainedViewLayoutTransition) -> CGFloat { - let params = Params(layout: layout, toolbar: toolbar, isTabBarHidden: self.tabBarHidden, currentControllerSearchState: self.currentController?.tabBarSearchState) + let params = Params(layout: layout, toolbar: toolbar, isTabBarHidden: self.tabBarHidden) if let layoutResult = self.layoutResult, layoutResult.params == params { return layoutResult.bottomInset } else { @@ -195,27 +179,18 @@ final class TabBarControllerNode: ASDisplayNode { } private func updateImpl(params: Params, transition: ContainedViewLayoutTransition) -> CGFloat { - var panelsBottomInset: CGFloat = params.layout.insets(options: []).bottom - if params.layout.metrics.widthClass == .regular, let inputHeight = params.layout.inputHeight, inputHeight != 0.0 { - panelsBottomInset = inputHeight + 8.0 + var options: ContainerViewLayoutInsetOptions = [] + if params.layout.metrics.widthClass == .regular { + options.insert(.input) } - if panelsBottomInset == 0.0 { - panelsBottomInset = 8.0 + + var bottomInset: CGFloat = params.layout.insets(options: options).bottom + if bottomInset == 0.0 { + bottomInset = 8.0 } else { - panelsBottomInset = max(panelsBottomInset, 8.0) - } - - var tabBarBottomInset: CGFloat = panelsBottomInset - if let currentController = self.currentController { - if let tabBarSearchState = currentController.tabBarSearchState, tabBarSearchState.isActive, let inputHeight = params.layout.inputHeight, inputHeight != 0.0 { - tabBarBottomInset = max(tabBarBottomInset, inputHeight + 8.0) - } - } - - var sideInset: CGFloat = 12.0 - if tabBarBottomInset <= 28.0 { - sideInset = 20.0 + bottomInset = max(bottomInset, 8.0) } + let sideInset: CGFloat = 20.0 var selectedId: AnyHashable? if self.selectedIndex < self.tabBarItems.count { @@ -233,7 +208,6 @@ final class TabBarControllerNode: ASDisplayNode { transition: tabBarTransition, component: AnyComponent(TabBarComponent( theme: self.theme, - strings: self.strings, items: self.tabBarItems.map { item in let itemId = AnyHashable(ObjectIdentifier(item.item)) return TabBarComponent.Item( @@ -256,30 +230,13 @@ final class TabBarControllerNode: ASDisplayNode { } ) }, - search: self.currentController?.tabBarSearchState.flatMap { tabBarSearchState in - return TabBarComponent.Search( - isActive: tabBarSearchState.isActive, - activate: { [weak self] in - guard let self else { - return - } - self.activateSearch() - }, - deactivate: { [weak self] in - guard let self else { - return - } - self.deactivateSearch() - } - ) - }, selectedId: selectedId, isTablet: params.layout.metrics.isTablet )), environment: {}, containerSize: CGSize(width: params.layout.size.width - sideInset * 2.0, height: 100.0) ) - let tabBarFrame = CGRect(origin: CGPoint(x: floor((params.layout.size.width - tabBarSize.width) * 0.5), y: params.layout.size.height - (self.tabBarHidden ? 0.0 : (tabBarSize.height + tabBarBottomInset))), size: tabBarSize) + let tabBarFrame = CGRect(origin: CGPoint(x: floor((params.layout.size.width - tabBarSize.width) * 0.5), y: params.layout.size.height - (self.tabBarHidden ? 0.0 : (tabBarSize.height + bottomInset))), size: tabBarSize) if let tabBarComponentView = self.tabBarView.view { if tabBarComponentView.superview == nil { @@ -291,13 +248,13 @@ final class TabBarControllerNode: ASDisplayNode { transition.updateFrame(node: self.disabledOverlayNode, frame: tabBarFrame) - let toolbarHeight = 50.0 + panelsBottomInset + let toolbarHeight = 50.0 + params.layout.insets(options: options).bottom let toolbarFrame = CGRect(origin: CGPoint(x: 0.0, y: params.layout.size.height - toolbarHeight), size: CGSize(width: params.layout.size.width, height: toolbarHeight)) if let toolbar = params.toolbar { if let toolbarNode = self.toolbarNode { transition.updateFrame(node: toolbarNode, frame: toolbarFrame) - toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: panelsBottomInset, toolbar: toolbar, transition: transition) + toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition) } else { let toolbarNode = ToolbarNode(theme: ToolbarTheme(theme: self.theme), displaySeparator: true, left: { [weak self] in self?.toolbarActionSelected(.left) @@ -307,7 +264,7 @@ final class TabBarControllerNode: ASDisplayNode { self?.toolbarActionSelected(.middle) }) toolbarNode.frame = toolbarFrame - toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: panelsBottomInset, toolbar: toolbar, transition: .immediate) + toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: .immediate) self.addSubnode(toolbarNode) self.toolbarNode = toolbarNode if transition.isAnimated { diff --git a/submodules/TabBarUI/Sources/TabBarController.swift b/submodules/TabBarUI/Sources/TabBarController.swift index 111fe208d1..a3f7fe0806 100644 --- a/submodules/TabBarUI/Sources/TabBarController.swift +++ b/submodules/TabBarUI/Sources/TabBarController.swift @@ -93,11 +93,9 @@ open class TabBarControllerImpl: ViewController, TabBarController { private let pendingControllerDisposable = MetaDisposable() private var theme: PresentationTheme - private var strings: PresentationStrings - public init(theme: PresentationTheme, strings: PresentationStrings) { + public init(theme: PresentationTheme) { self.theme = theme - self.strings = strings super.init(navigationBarPresentationData: nil) @@ -154,7 +152,7 @@ open class TabBarControllerImpl: ViewController, TabBarController { } override open func loadDisplayNode() { - self.displayNode = TabBarControllerNode(theme: self.theme, strings: self.strings, itemSelected: { [weak self] index, longTap, itemNodes in + self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in if let strongSelf = self { if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController { controller.presentTabBarPreviewingController(sourceNodes: itemNodes) @@ -237,16 +235,6 @@ open class TabBarControllerImpl: ViewController, TabBarController { self?.currentController?.toolbarActionSelected(action: action) }, disabledPressed: { [weak self] in self?.currentController?.tabBarDisabledAction() - }, activateSearch: { [weak self] in - guard let self else { - return - } - self.currentController?.tabBarActivateSearch() - }, deactivateSearch: { [weak self] in - guard let self else { - return - } - self.currentController?.tabBarDeactivateSearch() }) self.updateSelectedIndex() @@ -275,8 +263,7 @@ open class TabBarControllerImpl: ViewController, TabBarController { } if let currentController = self.currentController { currentController.willMove(toParent: nil) - currentController.tabBarSearchStateUpdated = nil - currentController.currentTabBarSearchNode = nil + //self.tabBarControllerNode.currentControllerNode = nil if animated { currentController.view.layer.animateScale(from: 1.0, to: transitionScale, duration: 0.12, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { completed in @@ -299,7 +286,7 @@ open class TabBarControllerImpl: ViewController, TabBarController { currentController.willMove(toParent: self) self.addChild(currentController) - let commit = self.tabBarControllerNode.setCurrentController(currentController) + let commit = self.tabBarControllerNode.setCurrentControllerNode(currentController.displayNode) if animated { currentController.view.layer.animateScale(from: transitionScale, to: 1.0, duration: 0.15, delay: 0.1, timingFunction: kCAMediaTimingFunctionSpring) currentController.view.layer.allowsGroupOpacity = true @@ -316,22 +303,6 @@ open class TabBarControllerImpl: ViewController, TabBarController { currentController.displayNode.recursivelyEnsureDisplaySynchronously(true) self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle - - currentController.tabBarSearchStateUpdated = { [weak self] transition in - guard let self else { - return - } - if let layout = self.validLayout { - self.containerLayoutUpdated(layout, transition: transition) - } - } - - currentController.currentTabBarSearchNode = { [weak self] in - guard let self else { - return nil - } - return self.tabBarControllerNode.currentSearchNode - } } if let layout = self.validLayout { diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index 99ef8a56b5..903663967c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -1174,9 +1174,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.touchDownGestureRecognizer = recognizer textInputNode.textView.accessibilityHint = self.textPlaceholderNode.attributedText?.string - - self.isAccessibilityContainer = true - self.accessibilityElements = [textInputNode.textView] } private func textFieldMaxHeight(_ maxHeight: CGFloat, metrics: LayoutMetrics, bottomInset: CGFloat) -> CGFloat { diff --git a/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/BUILD b/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/BUILD deleted file mode 100644 index 4805b62a3d..0000000000 --- a/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/BUILD +++ /dev/null @@ -1,26 +0,0 @@ -load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") - -swift_library( - name = "ChatListFilterTabContainerNode", - module_name = "ChatListFilterTabContainerNode", - srcs = glob([ - "Sources/**/*.swift", - ]), - copts = [ - "-warnings-as-errors", - ], - deps = [ - "//submodules/AsyncDisplayKit", - "//submodules/Display", - "//submodules/TelegramCore", - "//submodules/TelegramPresentationData", - "//submodules/TelegramUI/Components/TextNodeWithEntities", - "//submodules/AccountContext", - "//submodules/ComponentFlow", - "//submodules/TelegramUI/Components/GlassBackgroundComponent", - "//submodules/TelegramUI/Components/LiquidLens", - ], - visibility = [ - "//visibility:public", - ], -) diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD b/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD index 5355cc429f..6f94cb0c07 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD @@ -24,8 +24,6 @@ swift_library( "//submodules/Components/ComponentDisplayAdapters", "//submodules/SearchUI", "//submodules/TelegramUI/Components/MoreHeaderButton", - "//submodules/TelegramUI/Components/EdgeEffect", - "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift index 571c5cfe77..8e235da5ea 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift @@ -9,7 +9,6 @@ import AppBundle import StoryPeerListComponent import TelegramCore import MoreHeaderButton -import GlassBackgroundComponent public final class HeaderNetworkStatusComponent: Component { public enum Content: Equatable { @@ -80,6 +79,7 @@ public final class ChatListHeaderComponent: Component { public let chatListTitle: NetworkStatusTitle? public let leftButton: AnyComponentWithIdentity? public let rightButtons: [AnyComponentWithIdentity] + public let backTitle: String? public let backPressed: (() -> Void)? public init( @@ -89,6 +89,7 @@ public final class ChatListHeaderComponent: Component { chatListTitle: NetworkStatusTitle?, leftButton: AnyComponentWithIdentity?, rightButtons: [AnyComponentWithIdentity], + backTitle: String?, backPressed: (() -> Void)? ) { self.title = title @@ -97,6 +98,7 @@ public final class ChatListHeaderComponent: Component { self.chatListTitle = chatListTitle self.leftButton = leftButton self.rightButtons = rightButtons + self.backTitle = backTitle self.backPressed = backPressed } @@ -119,7 +121,7 @@ public final class ChatListHeaderComponent: Component { if lhs.rightButtons != rhs.rightButtons { return false } - if (lhs.backPressed == nil) != (rhs.backPressed == nil) { + if lhs.backTitle != rhs.backTitle { return false } return true @@ -224,6 +226,8 @@ public final class ChatListHeaderComponent: Component { private let onPressed: () -> Void let arrowView: UIImageView + let titleOffsetContainer: UIView + let titleView: ImmediateTextView private var currentColor: UIColor? @@ -231,11 +235,16 @@ public final class ChatListHeaderComponent: Component { self.onPressed = onPressed self.arrowView = UIImageView() + self.titleOffsetContainer = UIView() + self.titleView = ImmediateTextView() super.init(frame: CGRect()) self.addSubview(self.arrowView) + self.addSubview(self.titleOffsetContainer) + self.titleOffsetContainer.addSubview(self.titleView) + self.highligthedChanged = { [weak self] highlighted in guard let self else { return @@ -257,37 +266,37 @@ public final class ChatListHeaderComponent: Component { self.onPressed() } - func update(theme: PresentationTheme, strings: PresentationStrings, availableSize: CGSize, transition: ComponentTransition) -> CGSize { - self.accessibilityLabel = strings.Common_Back + func update(title: String, theme: PresentationTheme, availableSize: CGSize, transition: ComponentTransition) -> CGSize { + let titleText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor) + let titleTextUpdated = self.titleView.attributedText != titleText + self.titleView.attributedText = titleText + let titleSize = self.titleView.updateLayout(CGSize(width: 100.0, height: 44.0)) + + self.accessibilityLabel = title self.accessibilityTraits = [.button] - if self.currentColor != theme.chat.inputPanel.panelControlColor { - self.currentColor = theme.chat.inputPanel.panelControlColor - let imageSize = CGSize(width: 44.0, height: 44.0) - let topRightPoint = CGPoint(x: 24.6, y: 14.0) - let centerPoint = CGPoint(x: 17.0, y: imageSize.height * 0.5) - self.arrowView.image = generateImage(imageSize, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setStrokeColor(UIColor.white.cgColor) - context.setLineWidth(2.0) - context.setLineCap(.round) - context.setLineJoin(.round) - context.move(to: topRightPoint) - context.addLine(to: centerPoint) - context.addLine(to: CGPoint(x: topRightPoint.x, y: size.height - topRightPoint.y)) - context.strokePath() - })?.withRenderingMode(.alwaysTemplate) - self.arrowView.tintColor = theme.chat.inputPanel.panelControlColor + if self.currentColor != theme.rootController.navigationBar.accentTextColor { + self.currentColor = theme.rootController.navigationBar.accentTextColor + self.arrowView.image = NavigationBarTheme.generateBackArrowImage(color: theme.rootController.navigationBar.accentTextColor) } - let size = CGSize(width: 44.0, height: availableSize.height) + let iconSpacing: CGFloat = 8.0 + let iconOffset: CGFloat = -7.0 + let arrowSize = self.arrowView.image?.size ?? CGSize(width: 13.0, height: 22.0) - let arrowFrame = arrowSize.centered(in: CGRect(origin: CGPoint(), size: size)) + let arrowFrame = CGRect(origin: CGPoint(x: iconOffset - 1.0, y: floor((availableSize.height - arrowSize.height) / 2.0)), size: arrowSize) transition.setPosition(view: self.arrowView, position: arrowFrame.center) transition.setBounds(view: self.arrowView, bounds: CGRect(origin: CGPoint(), size: arrowFrame.size)) - return size + let titleFrame = CGRect(origin: CGPoint(x: iconOffset - 3.0 + arrowSize.width + iconSpacing, y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize) + if titleTextUpdated { + self.titleView.frame = titleFrame + } else { + transition.setFrame(view: self.titleView, frame: titleFrame) + } + + return CGSize(width: iconOffset + arrowSize.width + iconSpacing + titleSize.width, height: availableSize.height) } } @@ -296,9 +305,9 @@ public final class ChatListHeaderComponent: Component { let openStatusSetup: (UIView) -> Void let toggleIsLocked: () -> Void - let leftButtonsContainer: UIView + let leftButtonOffsetContainer: UIView var leftButtonViews: [AnyHashable: ComponentView] = [:] - let rightButtonsContainer: UIView + let rightButtonOffsetContainer: UIView var rightButtonViews: [AnyHashable: ComponentView] = [:] var backButtonView: BackButtonView? @@ -315,9 +324,6 @@ public final class ChatListHeaderComponent: Component { private(set) var centerContentOffsetX: CGFloat = 0.0 private(set) var centerContentOrigin: CGFloat = 0.0 - - private(set) var leftButtonsWidth: CGFloat = 0.0 - private(set) var rightButtonsWidth: CGFloat = 0.0 init( backPressed: @escaping () -> Void, @@ -328,9 +334,8 @@ public final class ChatListHeaderComponent: Component { self.openStatusSetup = openStatusSetup self.toggleIsLocked = toggleIsLocked - self.leftButtonsContainer = UIView() - self.rightButtonsContainer = UIView() - + self.leftButtonOffsetContainer = UIView() + self.rightButtonOffsetContainer = UIView() self.titleOffsetContainer = UIView() self.titleScaleContainer = UIView() @@ -340,6 +345,8 @@ public final class ChatListHeaderComponent: Component { self.addSubview(self.titleOffsetContainer) self.titleOffsetContainer.addSubview(self.titleScaleContainer) + self.addSubview(self.leftButtonOffsetContainer) + self.addSubview(self.rightButtonOffsetContainer) self.titleScaleContainer.addSubview(self.titleTextView) } @@ -386,12 +393,12 @@ public final class ChatListHeaderComponent: Component { transition.setSublayerTransform(view: self.titleOffsetContainer, transform: transform) } - func updateNavigationTransitionAsPrevious(nextView: ContentView, width: CGFloat, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) - alphaTransition.setAlpha(view: self.leftButtonsContainer, alpha: pow(1.0 - fraction, 2.0)) - alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: fraction * 10.0) - alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(1.0 - fraction, 2.0)) - alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: fraction * 10.0) + func updateNavigationTransitionAsPrevious(nextView: ContentView, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { + transition.setBounds(view: self.leftButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: fraction * self.bounds.width * 0.5, y: 0.0), size: self.leftButtonOffsetContainer.bounds.size), completion: { _ in + completion() + }) + transition.setAlpha(view: self.leftButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) + transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) if let backButtonView = self.backButtonView { transition.setBounds(view: backButtonView, bounds: CGRect(origin: CGPoint(x: fraction * self.bounds.width * 0.5, y: 0.0), size: backButtonView.bounds.size), completion: { _ in @@ -399,53 +406,80 @@ public final class ChatListHeaderComponent: Component { }) } - let totalOffset = -width * fraction - - transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: totalOffset * fraction, y: 0.0), size: self.titleOffsetContainer.bounds.size)) - transition.setAlpha(view: self.titleOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) + if let chatListTitleView = self.chatListTitleView, let nextBackButtonView = nextView.backButtonView { + let titleFrame = chatListTitleView.titleNode.view.convert(chatListTitleView.titleNode.bounds, to: self.titleOffsetContainer) + let backButtonTitleFrame = nextBackButtonView.convert(nextBackButtonView.titleView.frame, to: nextView) + + let totalOffset = titleFrame.minX - backButtonTitleFrame.minX + + transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: totalOffset * fraction, y: 0.0), size: self.titleOffsetContainer.bounds.size)) + transition.setAlpha(view: self.titleOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) + } } func updateNavigationTransitionAsNext(previousView: ContentView, storyPeerListView: StoryPeerListComponent.View?, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) - transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -(1.0 - fraction) * self.bounds.width, y: 0.0), size: self.titleOffsetContainer.bounds.size), completion: { _ in completion() }) - alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(fraction, 2.0)) - - alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) - alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) + transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(fraction, 2.0)) + transition.setBounds(view: self.rightButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: -(1.0 - fraction) * self.bounds.width, y: 0.0), size: self.rightButtonOffsetContainer.bounds.size)) + if let backButtonView = self.backButtonView { + transition.setScale(view: backButtonView.arrowView, scale: pow(max(0.001, fraction), 2.0)) + transition.setAlpha(view: backButtonView.arrowView, alpha: pow(fraction, 2.0)) + + if let storyPeerListView { + let previousTitleFrame = storyPeerListView.titleFrame() + let backButtonTitleFrame = backButtonView.convert(backButtonView.titleView.frame, to: self) + + let totalOffset = previousTitleFrame.minX - backButtonTitleFrame.minX + + transition.setBounds(view: backButtonView.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -totalOffset * (1.0 - fraction), y: 0.0), size: backButtonView.titleOffsetContainer.bounds.size)) + transition.setAlpha(view: backButtonView.titleOffsetContainer, alpha: pow(fraction, 2.0)) + } else if let previousChatListTitleView = previousView.chatListTitleView { + let previousTitleFrame = previousChatListTitleView.titleNode.view.convert(previousChatListTitleView.titleNode.bounds, to: previousView.titleOffsetContainer) + let backButtonTitleFrame = backButtonView.convert(backButtonView.titleView.frame, to: self) + + let totalOffset = previousTitleFrame.minX - backButtonTitleFrame.minX + + transition.setBounds(view: backButtonView.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -totalOffset * (1.0 - fraction), y: 0.0), size: backButtonView.titleOffsetContainer.bounds.size)) + transition.setAlpha(view: backButtonView.titleOffsetContainer, alpha: pow(fraction, 2.0)) + } + } } func updateNavigationTransitionAsPreviousInplace(nextView: ContentView, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) - - alphaTransition.setAlpha(view: self.leftButtonsContainer, alpha: pow(1.0 - fraction, 2.0)) - alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: fraction * 10.0) - alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(1.0 - fraction, 2.0), completion: { _ in + transition.setBounds(view: self.leftButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.leftButtonOffsetContainer.bounds.size), completion: { _ in + }) + transition.setAlpha(view: self.leftButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) + transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0), completion: { _ in completion() }) - alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: fraction * 10.0) + + if let backButtonView = self.backButtonView { + transition.setBounds(view: backButtonView, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backButtonView.bounds.size), completion: { _ in + }) + } transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.titleOffsetContainer.bounds.size)) transition.setAlpha(view: self.titleOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) } func updateNavigationTransitionAsNextInplace(previousView: ContentView, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) - transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.titleOffsetContainer.bounds.size), completion: { _ in completion() }) - alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) - alphaTransition.setAlpha(view: self.leftButtonsContainer, alpha: pow(fraction, 2.0)) - alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) - alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(fraction, 2.0)) + transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(fraction, 2.0)) + transition.setBounds(view: self.rightButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.rightButtonOffsetContainer.bounds.size)) + if let backButtonView = self.backButtonView { + transition.setScale(view: backButtonView.arrowView, scale: pow(max(0.001, fraction), 2.0)) + transition.setAlpha(view: backButtonView.arrowView, alpha: pow(fraction, 2.0)) + + transition.setBounds(view: backButtonView.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backButtonView.titleOffsetContainer.bounds.size)) + transition.setAlpha(view: backButtonView.titleOffsetContainer, alpha: pow(fraction, 2.0)) + } } - func update(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, content: Content, displayBackButton: Bool, sideInset: CGFloat, sideContentWidth: CGFloat, sideContentFraction: CGFloat, size: CGSize, transition: ComponentTransition) { - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) - + func update(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, content: Content, backTitle: String?, sideInset: CGFloat, sideContentWidth: CGFloat, sideContentFraction: CGFloat, size: CGSize, transition: ComponentTransition) { transition.setPosition(view: self.titleOffsetContainer, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: self.titleOffsetContainer.bounds.origin, size: size)) @@ -456,10 +490,11 @@ public final class ChatListHeaderComponent: Component { let titleTextUpdated = self.titleTextView.attributedText != titleText self.titleTextView.attributedText = titleText - let buttonSpacing: CGFloat = 0.0 - var nextLeftButtonX: CGFloat = 0.0 + let buttonSpacing: CGFloat = 8.0 - if displayBackButton { + var leftOffset = sideInset + + if let backTitle = backTitle { var backButtonTransition = transition let backButtonView: BackButtonView if let current = self.backButtonView { @@ -473,14 +508,11 @@ public final class ChatListHeaderComponent: Component { self.backPressed() }) self.backButtonView = backButtonView - self.leftButtonsContainer.addSubview(backButtonView) + self.addSubview(backButtonView) } - let backButtonSize = backButtonView.update(theme: theme, strings: strings, availableSize: CGSize(width: 100.0, height: size.height), transition: backButtonTransition) - backButtonTransition.setFrame(view: backButtonView, frame: CGRect(origin: CGPoint(x: nextLeftButtonX, y: floor((size.height - backButtonSize.height) / 2.0)), size: backButtonSize)) - if nextLeftButtonX != 0.0 { - nextLeftButtonX += buttonSpacing - } - nextLeftButtonX += backButtonSize.width + let backButtonSize = backButtonView.update(title: backTitle, theme: theme, availableSize: CGSize(width: 100.0, height: size.height), transition: backButtonTransition) + backButtonTransition.setFrame(view: backButtonView, frame: CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - backButtonSize.height) / 2.0)), size: backButtonSize)) + leftOffset += backButtonSize.width + buttonSpacing } else if let backButtonView = self.backButtonView { self.backButtonView = nil backButtonView.removeFromSuperview() @@ -489,10 +521,6 @@ public final class ChatListHeaderComponent: Component { var validLeftButtons = Set() if let leftButton = content.leftButton { validLeftButtons.insert(leftButton.id) - - if nextLeftButtonX != 0.0 { - nextLeftButtonX += buttonSpacing - } var buttonTransition = transition var animateButtonIn = false @@ -513,25 +541,23 @@ public final class ChatListHeaderComponent: Component { }, containerSize: CGSize(width: 100.0, height: size.height) ) - let buttonFrame = CGRect(origin: CGPoint(x: nextLeftButtonX, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) + let buttonFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) if let buttonComponentView = buttonView.view { if buttonComponentView.superview == nil { - self.leftButtonsContainer.addSubview(buttonComponentView) + self.leftButtonOffsetContainer.addSubview(buttonComponentView) } buttonTransition.setFrame(view: buttonComponentView, frame: buttonFrame) if animateButtonIn { - alphaTransition.animateBlur(layer: buttonComponentView.layer, fromRadius: 10.0, toRadius: 0.0) - alphaTransition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) + transition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) } } - nextLeftButtonX += buttonSize.width + leftOffset = buttonFrame.maxX + buttonSpacing } var removeLeftButtons: [AnyHashable] = [] for (id, buttonView) in self.leftButtonViews { if !validLeftButtons.contains(id) { if let buttonComponentView = buttonView.view { - alphaTransition.setBlur(layer: buttonComponentView.layer, radius: 10.0) - alphaTransition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in + transition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in buttonComponentView?.removeFromSuperview() }) } @@ -542,14 +568,10 @@ public final class ChatListHeaderComponent: Component { self.leftButtonViews.removeValue(forKey: id) } - var nextRightButtonX: CGFloat = 0.0 + var rightOffset = size.width - sideInset var validRightButtons = Set() - for rightButton in content.rightButtons.reversed() { + for rightButton in content.rightButtons { validRightButtons.insert(rightButton.id) - - if nextRightButtonX != 0.0 { - nextRightButtonX += buttonSpacing - } var buttonTransition = transition var animateButtonIn = false @@ -570,25 +592,23 @@ public final class ChatListHeaderComponent: Component { }, containerSize: CGSize(width: 100.0, height: size.height) ) - let buttonFrame = CGRect(origin: CGPoint(x: nextRightButtonX, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) + let buttonFrame = CGRect(origin: CGPoint(x: rightOffset - buttonSize.width, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) if let buttonComponentView = buttonView.view { if buttonComponentView.superview == nil { - self.rightButtonsContainer.addSubview(buttonComponentView) + self.rightButtonOffsetContainer.addSubview(buttonComponentView) } buttonTransition.setFrame(view: buttonComponentView, frame: buttonFrame) if animateButtonIn { - alphaTransition.animateBlur(layer: buttonComponentView.layer, fromRadius: 10.0, toRadius: 0.0) - alphaTransition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) + transition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) } } - nextRightButtonX += buttonSize.width + rightOffset = buttonFrame.minX - buttonSpacing } var removeRightButtons: [AnyHashable] = [] for (id, buttonView) in self.rightButtonViews { if !validRightButtons.contains(id) { if let buttonComponentView = buttonView.view { - alphaTransition.setBlur(layer: buttonComponentView.layer, radius: 10.0) - alphaTransition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in + transition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in buttonComponentView?.removeFromSuperview() }) } @@ -598,11 +618,8 @@ public final class ChatListHeaderComponent: Component { for id in removeRightButtons { self.rightButtonViews.removeValue(forKey: id) } - - self.leftButtonsWidth = nextLeftButtonX - self.rightButtonsWidth = nextRightButtonX - - let commonInset: CGFloat = sideInset + max(nextLeftButtonX, nextRightButtonX) + 8.0 + + let commonInset: CGFloat = max(leftOffset, size.width - rightOffset) let remainingWidth = size.width - commonInset * 2.0 let titleTextSize = self.titleTextView.updateLayout(CGSize(width: remainingWidth, height: size.height)) @@ -645,10 +662,10 @@ public final class ChatListHeaderComponent: Component { } var centerContentLeftInset: CGFloat = 0.0 - centerContentLeftInset = nextLeftButtonX + 4.0 + centerContentLeftInset = leftOffset - 4.0 var centerContentRightInset: CGFloat = 0.0 - centerContentRightInset = nextRightButtonX + 20.0 + centerContentRightInset = size.width - rightOffset - 8.0 var centerContentWidth: CGFloat = 0.0 var centerContentOffsetX: CGFloat = 0.0 @@ -673,8 +690,10 @@ public final class ChatListHeaderComponent: Component { let titleContentRect = chatListTitleView.updateLayout(size: chatListTitleContentSize, clearBounds: CGRect(origin: CGPoint(), size: chatListTitleContentSize), transition: transition.containedViewLayoutTransition) centerContentWidth = floor((chatListTitleContentSize.width * 0.5 - titleContentRect.minX) * 2.0) + //sideWidth + centerWidth + centerOffset = size.width + //let centerOffset = -(size.width - (sideContentWidth + centerContentWidth)) * 0.5 + size.width * 0.5 let centerOffset = sideContentWidth * 0.5 - centerContentOffsetX = -max(0.0, centerOffset + titleContentRect.maxX - 2.0 - (size.width - sideInset - nextRightButtonX)) + centerContentOffsetX = -max(0.0, centerOffset + titleContentRect.maxX - 2.0 - rightOffset) chatListTitleView.openStatusSetup = { [weak self] sourceView in guard let self else { @@ -722,11 +741,6 @@ public final class ChatListHeaderComponent: Component { private var primaryContentView: ContentView? private var secondaryContentView: ContentView? private var storyOffsetFraction: CGFloat = 0.0 - - private let leftButtonsContainer: UIView - private let rightButtonsContainer: UIView - private var leftButtonsBackgroundContainer: GlassBackgroundView? - private var rightButtonsBackgroundContainer: GlassBackgroundView? private let storyPeerListExternalState = StoryPeerListComponent.ExternalState() private var storyPeerList: ComponentView? @@ -739,10 +753,6 @@ public final class ChatListHeaderComponent: Component { } override init(frame: CGRect) { - self.leftButtonsContainer = UIView() - self.rightButtonsContainer = UIView() - self.rightButtonsContainer.layer.anchorPoint = CGPoint(x: 1.0, y: 0.0) - super.init(frame: frame) self.storyOffsetFraction = 1.0 @@ -756,6 +766,10 @@ public final class ChatListHeaderComponent: Component { return self.effectiveContentView?.backButtonView?.arrowView } + public var backButtonTitleView: UIView? { + return self.effectiveContentView?.backButtonView?.titleView + } + public var rightButtonView: UIView? { return self.effectiveContentView?.rightButtonViews.first?.value.view } @@ -781,6 +795,18 @@ public final class ChatListHeaderComponent: Component { } } + public func makeTransitionBackButtonView(accentColor: UIColor) -> UIView? { + if let backButtonTitleView = self.backButtonTitleView as? ImmediateTextView { + let view = ImmediateTextView() + view.attributedText = NSAttributedString(string: backButtonTitleView.attributedText?.string ?? "", font: Font.regular(17.0), textColor: accentColor) + let _ = view.updateLayout(CGSize(width: 100.0, height: 100.0)) + view.frame = backButtonTitleView.convert(backButtonTitleView.bounds, to: self) + return view + } else { + return nil + } + } + public func storyPeerListView() -> StoryPeerListComponent.View? { return self.storyPeerList?.view as? StoryPeerListComponent.View } @@ -822,8 +848,8 @@ public final class ChatListHeaderComponent: Component { let previousComponent = self.component self.component = component - var primaryContentTransition = transition if var primaryContent = component.primaryContent { + var primaryContentTransition = transition let primaryContentView: ContentView if let current = self.primaryContentView { primaryContentView = current @@ -851,8 +877,6 @@ public final class ChatListHeaderComponent: Component { ) self.primaryContentView = primaryContentView self.addSubview(primaryContentView) - self.leftButtonsContainer.addSubview(primaryContentView.leftButtonsContainer) - self.rightButtonsContainer.addSubview(primaryContentView.rightButtonsContainer) } let sideContentWidth: CGFloat = 0.0 @@ -865,19 +889,18 @@ public final class ChatListHeaderComponent: Component { chatListTitle: nil, leftButton: primaryContent.leftButton, rightButtons: primaryContent.rightButtons, + backTitle: primaryContent.backTitle, backPressed: primaryContent.backPressed ) } - primaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: primaryContent, displayBackButton: primaryContent.backPressed != nil, sideInset: component.sideInset, sideContentWidth: sideContentWidth, sideContentFraction: (1.0 - component.storiesFraction), size: availableSize, transition: primaryContentTransition) + primaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: primaryContent, backTitle: primaryContent.backTitle, sideInset: component.sideInset, sideContentWidth: sideContentWidth, sideContentFraction: (1.0 - component.storiesFraction), size: availableSize, transition: primaryContentTransition) primaryContentTransition.setFrame(view: primaryContentView, frame: CGRect(origin: CGPoint(), size: availableSize)) primaryContentView.updateContentOffsetFraction(contentOffsetFraction: 1.0 - self.storyOffsetFraction, transition: primaryContentTransition) } else if let primaryContentView = self.primaryContentView { self.primaryContentView = nil primaryContentView.removeFromSuperview() - primaryContentView.leftButtonsContainer.removeFromSuperview() - primaryContentView.rightButtonsContainer.removeFromSuperview() } var storyListTransition = transition @@ -968,16 +991,13 @@ public final class ChatListHeaderComponent: Component { ) } - var secondaryContentTransition = transition - var secondaryContentIsAnimatingIn = false - var removedSecondaryContentView: ContentView? if let secondaryContent = component.secondaryContent { + var secondaryContentTransition = transition let secondaryContentView: ContentView if let current = self.secondaryContentView { secondaryContentView = current } else { secondaryContentTransition = .immediate - secondaryContentIsAnimatingIn = true secondaryContentView = ContentView( backPressed: { [weak self] in guard let self, let component = self.component else { @@ -999,15 +1019,9 @@ public final class ChatListHeaderComponent: Component { } ) self.secondaryContentView = secondaryContentView - if let primaryContentView = self.primaryContentView { - self.insertSubview(secondaryContentView, aboveSubview: primaryContentView) - } else { - self.addSubview(secondaryContentView) - } - self.leftButtonsContainer.addSubview(secondaryContentView.leftButtonsContainer) - self.rightButtonsContainer.addSubview(secondaryContentView.rightButtonsContainer) + self.addSubview(secondaryContentView) } - secondaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: secondaryContent, displayBackButton: true, sideInset: component.sideInset, sideContentWidth: 0.0, sideContentFraction: 0.0, size: availableSize, transition: secondaryContentTransition) + secondaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: secondaryContent, backTitle: component.primaryContent?.navigationBackTitle ?? component.primaryContent?.title, sideInset: component.sideInset, sideContentWidth: 0.0, sideContentFraction: 0.0, size: availableSize, transition: secondaryContentTransition) secondaryContentTransition.setFrame(view: secondaryContentView, frame: CGRect(origin: CGPoint(), size: availableSize)) secondaryContentView.updateContentOffsetFraction(contentOffsetFraction: 1.0 - self.storyOffsetFraction, transition: secondaryContentTransition) @@ -1018,7 +1032,7 @@ public final class ChatListHeaderComponent: Component { primaryContentView.updateNavigationTransitionAsPreviousInplace(nextView: secondaryContentView, fraction: 0.0, transition: .immediate, completion: {}) secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: 0.0, transition: .immediate, completion: {}) } else { - primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, width: availableSize.width, fraction: 0.0, transition: .immediate, completion: {}) + primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: 0.0, transition: .immediate, completion: {}) secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: 0.0, transition: .immediate, completion: {}) } } @@ -1027,33 +1041,26 @@ public final class ChatListHeaderComponent: Component { primaryContentView.updateNavigationTransitionAsPreviousInplace(nextView: secondaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) } else { - primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, width: availableSize.width, fraction: component.secondaryTransition, transition: transition, completion: {}) + primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: component.secondaryTransition, transition: transition, completion: {}) } } } else if let secondaryContentView = self.secondaryContentView { self.secondaryContentView = nil - removedSecondaryContentView = secondaryContentView if let primaryContentView = self.primaryContentView { if self.storyOffsetFraction < 0.8 { primaryContentView.updateNavigationTransitionAsPreviousInplace(nextView: secondaryContentView, fraction: 0.0, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: 0.0, transition: transition, completion: { [weak secondaryContentView] in - secondaryContentView?.leftButtonsContainer.removeFromSuperview() - secondaryContentView?.rightButtonsContainer.removeFromSuperview() secondaryContentView?.removeFromSuperview() }) } else { - primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, width: availableSize.width, fraction: 0.0, transition: transition, completion: {}) + primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: 0.0, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: 0.0, transition: transition, completion: { [weak secondaryContentView] in - secondaryContentView?.leftButtonsContainer.removeFromSuperview() - secondaryContentView?.rightButtonsContainer.removeFromSuperview() secondaryContentView?.removeFromSuperview() }) } } else { - secondaryContentView.leftButtonsContainer.removeFromSuperview() - secondaryContentView.rightButtonsContainer.removeFromSuperview() secondaryContentView.removeFromSuperview() } } @@ -1063,10 +1070,18 @@ public final class ChatListHeaderComponent: Component { self.addSubview(storyPeerListComponentView) } + //let storyPeerListMinOffset: CGFloat = -7.0 let storyPeerListMaxOffset: CGFloat = availableSize.height + 2.0 + //let storyPeerListPosition: CGFloat = storyPeerListMinOffset * (1.0 - component.storiesFraction) + storyPeerListMaxOffset * component.storiesFraction + var storiesX: CGFloat = 0.0 - storiesX -= availableSize.width * component.secondaryTransition + if let nextBackButtonView = self.secondaryContentView?.backButtonView { + let backButtonTitleFrame = nextBackButtonView.convert(nextBackButtonView.titleView.frame, to: self) + let storyListTitleFrame = storyPeerListComponentView.titleFrame() + + storiesX += (backButtonTitleFrame.minX - storyListTitleFrame.minX) * component.secondaryTransition + } storyListTransition.setFrame(view: storyPeerListComponentView, frame: CGRect(origin: CGPoint(x: storiesX, y: storyPeerListMaxOffset), size: CGSize(width: availableSize.width, height: 79.0))) @@ -1075,84 +1090,6 @@ public final class ChatListHeaderComponent: Component { let storyListAlpha: CGFloat = (1.0 - component.secondaryTransition) * storyListNormalAlpha storyListTransition.setAlpha(view: storyPeerListComponentView, alpha: storyListAlpha) } - - var leftButtonsEffectiveWidth: CGFloat = 0.0 - var rightButtonsEffectiveWidth: CGFloat = 0.0 - if let primaryContentView = self.primaryContentView, let secondaryContentView = self.secondaryContentView { - - leftButtonsEffectiveWidth = primaryContentView.leftButtonsWidth * (1.0 - component.secondaryTransition) + secondaryContentView.leftButtonsWidth * component.secondaryTransition - rightButtonsEffectiveWidth = primaryContentView.rightButtonsWidth * (1.0 - component.secondaryTransition) + secondaryContentView.rightButtonsWidth * component.secondaryTransition - - primaryContentTransition.setFrame(view: primaryContentView.leftButtonsContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.leftButtonsWidth), height: 44.0))) - secondaryContentTransition.setFrame(view: secondaryContentView.leftButtonsContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: max(44.0, secondaryContentView.leftButtonsWidth), height: 44.0))) - - primaryContentTransition.setFrame(view: primaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - primaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.rightButtonsWidth), height: 44.0))) - - if secondaryContentIsAnimatingIn { - secondaryContentView.rightButtonsContainer.frame = CGRect(origin: CGPoint(x: self.rightButtonsContainer.bounds.width - secondaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, secondaryContentView.rightButtonsWidth), height: 44.0)) - } - transition.setFrame(view: secondaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - secondaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, secondaryContentView.rightButtonsWidth), height: 44.0))) - } else if let primaryContentView = self.primaryContentView { - leftButtonsEffectiveWidth = primaryContentView.leftButtonsWidth - rightButtonsEffectiveWidth = primaryContentView.rightButtonsWidth - - primaryContentTransition.setFrame(view: primaryContentView.leftButtonsContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.leftButtonsWidth), height: 44.0))) - primaryContentTransition.setFrame(view: primaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - primaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.rightButtonsWidth), height: 44.0))) - - if let removedSecondaryContentView { - transition.setFrame(view: removedSecondaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - removedSecondaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, removedSecondaryContentView.rightButtonsWidth), height: 44.0))) - } - } - - if leftButtonsEffectiveWidth != 0.0 { - let leftButtonsBackgroundContainer: GlassBackgroundView - var leftButtonsBackgroundContainerTransition = transition - if let current = self.leftButtonsBackgroundContainer { - leftButtonsBackgroundContainer = current - } else { - leftButtonsBackgroundContainerTransition = leftButtonsBackgroundContainerTransition.withAnimation(.none) - leftButtonsBackgroundContainer = GlassBackgroundView() - self.leftButtonsBackgroundContainer = leftButtonsBackgroundContainer - self.addSubview(leftButtonsBackgroundContainer) - leftButtonsBackgroundContainer.contentView.addSubview(self.leftButtonsContainer) - } - let leftButtonsContainerFrame = CGRect(origin: CGPoint(x: component.sideInset, y: 0.0), size: CGSize(width: max(44.0, leftButtonsEffectiveWidth), height: 44.0)) - leftButtonsBackgroundContainerTransition.setFrame(view: leftButtonsBackgroundContainer, frame: leftButtonsContainerFrame) - leftButtonsBackgroundContainer.update(size: leftButtonsContainerFrame.size, cornerRadius: leftButtonsContainerFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: leftButtonsBackgroundContainerTransition) - leftButtonsBackgroundContainerTransition.setFrame(view: self.leftButtonsContainer, frame: CGRect(origin: CGPoint(), size: leftButtonsContainerFrame.size)) - } else { - if let leftButtonsBackgroundContainer = self.leftButtonsBackgroundContainer { - self.leftButtonsBackgroundContainer = nil - transition.setAlpha(view: leftButtonsBackgroundContainer, alpha: 0.0, completion: { [weak leftButtonsBackgroundContainer] _ in - leftButtonsBackgroundContainer?.removeFromSuperview() - }) - } - } - - if rightButtonsEffectiveWidth != 0.0 { - let rightButtonsBackgroundContainer: GlassBackgroundView - var rightButtonsBackgroundContainerTransition = transition - if let current = self.rightButtonsBackgroundContainer { - rightButtonsBackgroundContainer = current - } else { - rightButtonsBackgroundContainerTransition = rightButtonsBackgroundContainerTransition.withAnimation(.none) - rightButtonsBackgroundContainer = GlassBackgroundView() - self.rightButtonsBackgroundContainer = rightButtonsBackgroundContainer - self.addSubview(rightButtonsBackgroundContainer) - rightButtonsBackgroundContainer.contentView.addSubview(self.rightButtonsContainer) - } - let rightButtonsContainerFrame = CGRect(origin: CGPoint(x: availableSize.width - component.sideInset - max(44.0, rightButtonsEffectiveWidth), y: 0.0), size: CGSize(width: max(44.0, rightButtonsEffectiveWidth), height: 44.0)) - rightButtonsBackgroundContainerTransition.setFrame(view: rightButtonsBackgroundContainer, frame: rightButtonsContainerFrame) - rightButtonsBackgroundContainer.update(size: rightButtonsContainerFrame.size, cornerRadius: rightButtonsContainerFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: rightButtonsBackgroundContainerTransition) - rightButtonsBackgroundContainerTransition.setFrame(view: self.rightButtonsContainer, frame: CGRect(origin: CGPoint(), size: rightButtonsContainerFrame.size)) - } else { - if let rightButtonsBackgroundContainer = self.rightButtonsBackgroundContainer { - self.rightButtonsBackgroundContainer = nil - transition.setAlpha(view: rightButtonsBackgroundContainer, alpha: 0.0, completion: { [weak rightButtonsBackgroundContainer] _ in - rightButtonsBackgroundContainer?.removeFromSuperview() - }) - } - } return availableSize } @@ -1182,3 +1119,251 @@ public final class ChatListHeaderComponent: Component { return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } } + +public final class NavigationButtonComponentEnvironment: Equatable { + public let theme: PresentationTheme + + public init(theme: PresentationTheme) { + self.theme = theme + } + + public static func ==(lhs: NavigationButtonComponentEnvironment, rhs: NavigationButtonComponentEnvironment) -> Bool { + if lhs.theme != rhs.theme { + return false + } + return true + } +} + +public final class NavigationButtonComponent: Component { + public typealias EnvironmentType = NavigationButtonComponentEnvironment + + public enum Content: Equatable { + case text(title: String, isBold: Bool) + case more + case icon(imageName: String) + case proxy(status: ChatTitleProxyStatus) + } + + public let content: Content + public let pressed: (UIView) -> Void + public let contextAction: ((UIView, ContextGesture?) -> Void)? + + public init( + content: Content, + pressed: @escaping (UIView) -> Void, + contextAction: ((UIView, ContextGesture?) -> Void)? = nil + ) { + self.content = content + self.pressed = pressed + self.contextAction = contextAction + } + + public static func ==(lhs: NavigationButtonComponent, rhs: NavigationButtonComponent) -> Bool { + if lhs.content != rhs.content { + return false + } + return true + } + + public final class View: HighlightTrackingButton { + private var textView: ImmediateTextView? + + private var iconView: UIImageView? + private var iconImageName: String? + + private var proxyNode: ChatTitleProxyNode? + + private var moreButton: MoreHeaderButton? + + private var component: NavigationButtonComponent? + private var theme: PresentationTheme? + + override init(frame: CGRect) { + super.init(frame: frame) + + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + + self.highligthedChanged = { [weak self] highlighted in + guard let self else { + return + } + if highlighted { + self.textView?.alpha = 0.6 + self.proxyNode?.alpha = 0.6 + self.iconView?.alpha = 0.6 + } else { + self.textView?.alpha = 1.0 + self.textView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) + + self.proxyNode?.alpha = 1.0 + self.proxyNode?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) + + self.iconView?.alpha = 1.0 + self.iconView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) + } + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func pressed() { + self.component?.pressed(self) + } + + func update(component: NavigationButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + let theme = environment[NavigationButtonComponentEnvironment.self].value.theme + var themeUpdated = false + if self.theme !== theme { + self.theme = theme + themeUpdated = true + } + + let iconOffset: CGFloat = 4.0 + + var textString: NSAttributedString? + var imageName: String? + var proxyStatus: ChatTitleProxyStatus? + var isMore: Bool = false + + switch component.content { + case let .text(title, isBold): + textString = NSAttributedString(string: title, font: isBold ? Font.bold(17.0) : Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor) + case .more: + isMore = true + case let .icon(imageNameValue): + imageName = imageNameValue + case let .proxy(status): + proxyStatus = status + } + + var size = CGSize(width: 0.0, height: availableSize.height) + + if let textString = textString { + let textView: ImmediateTextView + if let current = self.textView { + textView = current + } else { + textView = ImmediateTextView() + textView.isUserInteractionEnabled = false + self.textView = textView + self.addSubview(textView) + } + + textView.attributedText = textString + let textSize = textView.updateLayout(availableSize) + size.width = textSize.width + + textView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((availableSize.height - textSize.height) / 2.0)), size: textSize) + } else if let textView = self.textView { + self.textView = nil + textView.removeFromSuperview() + } + + if let imageName = imageName { + let iconView: UIImageView + if let current = self.iconView { + iconView = current + } else { + iconView = UIImageView() + iconView.isUserInteractionEnabled = false + self.iconView = iconView + self.addSubview(iconView) + } + if self.iconImageName != imageName || themeUpdated { + self.iconImageName = imageName + iconView.image = generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.rootController.navigationBar.accentTextColor) + } + + if let iconSize = iconView.image?.size { + size.width = iconSize.width + + iconView.frame = CGRect(origin: CGPoint(x: iconOffset, y: floor((availableSize.height - iconSize.height) / 2.0)), size: iconSize) + } + } else if let iconView = self.iconView { + self.iconView = nil + iconView.removeFromSuperview() + self.iconImageName = nil + } + + if let proxyStatus = proxyStatus { + let proxyNode: ChatTitleProxyNode + if let current = self.proxyNode { + proxyNode = current + } else { + proxyNode = ChatTitleProxyNode(theme: theme) + proxyNode.isUserInteractionEnabled = false + self.proxyNode = proxyNode + self.addSubnode(proxyNode) + } + + let proxySize = CGSize(width: 30.0, height: 30.0) + size.width = proxySize.width + + proxyNode.theme = theme + proxyNode.status = proxyStatus + + proxyNode.frame = CGRect(origin: CGPoint(x: iconOffset, y: floor((availableSize.height - proxySize.height) / 2.0)), size: proxySize) + } else if let proxyNode = self.proxyNode { + self.proxyNode = nil + proxyNode.removeFromSupernode() + } + + if isMore { + let moreButton: MoreHeaderButton + if let current = self.moreButton, !themeUpdated { + moreButton = current + } else { + if let moreButton = self.moreButton { + moreButton.removeFromSupernode() + self.moreButton = nil + } + + moreButton = MoreHeaderButton(color: theme.rootController.navigationBar.buttonColor) + moreButton.isUserInteractionEnabled = true + moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.rootController.navigationBar.buttonColor))) + moreButton.onPressed = { [weak self] in + guard let self, let component = self.component else { + return + } + self.moreButton?.play() + component.pressed(self) + } + moreButton.contextAction = { [weak self] sourceNode, gesture in + guard let self, let component = self.component else { + return + } + self.moreButton?.play() + component.contextAction?(self, gesture) + } + self.moreButton = moreButton + self.addSubnode(moreButton) + } + + let buttonSize = CGSize(width: 26.0, height: 44.0) + size.width = buttonSize.width + + moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.rootController.navigationBar.buttonColor))) + + moreButton.frame = CGRect(origin: CGPoint(x: iconOffset, y: floor((availableSize.height - buttonSize.height) / 2.0)), size: buttonSize) + } else if let moreButton = self.moreButton { + self.moreButton = nil + moreButton.removeFromSupernode() + } + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift index dc26cb74e3..549c6fb4fd 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift @@ -9,7 +9,6 @@ import SearchUI import AccountContext import TelegramCore import StoryPeerListComponent -import EdgeEffect public final class ChatListNavigationBar: Component { public final class AnimationHint { @@ -21,22 +20,14 @@ public final class ChatListNavigationBar: Component { self.crossfadeStoryPeers = crossfadeStoryPeers } } - - public struct Search: Equatable { - public var isEnabled: Bool - - public init(isEnabled: Bool) { - self.isEnabled = isEnabled - } - } public let context: AccountContext public let theme: PresentationTheme public let strings: PresentationStrings public let statusBarHeight: CGFloat public let sideInset: CGFloat - public let search: Search? public let isSearchActive: Bool + public let isSearchEnabled: Bool public let primaryContent: ChatListHeaderComponent.Content? public let secondaryContent: ChatListHeaderComponent.Content? public let secondaryTransition: CGFloat @@ -57,8 +48,8 @@ public final class ChatListNavigationBar: Component { strings: PresentationStrings, statusBarHeight: CGFloat, sideInset: CGFloat, - search: Search?, isSearchActive: Bool, + isSearchEnabled: Bool, primaryContent: ChatListHeaderComponent.Content?, secondaryContent: ChatListHeaderComponent.Content?, secondaryTransition: CGFloat, @@ -78,8 +69,8 @@ public final class ChatListNavigationBar: Component { self.strings = strings self.statusBarHeight = statusBarHeight self.sideInset = sideInset - self.search = search self.isSearchActive = isSearchActive + self.isSearchEnabled = isSearchEnabled self.primaryContent = primaryContent self.secondaryContent = secondaryContent self.secondaryTransition = secondaryTransition @@ -111,10 +102,10 @@ public final class ChatListNavigationBar: Component { if lhs.sideInset != rhs.sideInset { return false } - if lhs.search != rhs.search { + if lhs.isSearchActive != rhs.isSearchActive { return false } - if lhs.isSearchActive != rhs.isSearchActive { + if lhs.isSearchEnabled != rhs.isSearchEnabled { return false } if lhs.primaryContent != rhs.primaryContent { @@ -164,7 +155,8 @@ public final class ChatListNavigationBar: Component { }() public final class View: UIView { - private let edgeEffectView: EdgeEffectView + private let backgroundView: BlurredBackgroundView + private let separatorLayer: SimpleLayer public let headerContent = ComponentView() @@ -192,15 +184,17 @@ public final class ChatListNavigationBar: Component { private var disappearingTabsViewSearch: Bool = false private var currentHeaderComponent: ChatListHeaderComponent? - - private var currentHeight: CGFloat = 0.0 override public init(frame: CGRect) { - self.edgeEffectView = EdgeEffectView() + self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) + self.backgroundView.layer.anchorPoint = CGPoint(x: 0.0, y: 1.0) + self.separatorLayer = SimpleLayer() + self.separatorLayer.anchorPoint = CGPoint() super.init(frame: frame) - self.addSubview(self.edgeEffectView) + self.addSubview(self.backgroundView) + self.layer.addSublayer(self.separatorLayer) } required init?(coder: NSCoder) { @@ -208,7 +202,7 @@ public final class ChatListNavigationBar: Component { } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if point.y >= self.currentHeight { + if !self.backgroundView.frame.contains(point) { return nil } @@ -254,12 +248,7 @@ public final class ChatListNavigationBar: Component { let searchOffsetDistance: CGFloat = ChatListNavigationBar.searchScrollHeight - let minContentOffset: CGFloat - if component.search != nil { - minContentOffset = ChatListNavigationBar.searchScrollHeight - } else { - minContentOffset = 0.0 - } + let minContentOffset: CGFloat = ChatListNavigationBar.searchScrollHeight let clippedScrollOffset = min(minContentOffset, offset) if self.clippedScrollOffset == clippedScrollOffset && !self.hasDeferredScrollOffset && !forceUpdate && !allowAvatarsExpansionUpdated { @@ -270,87 +259,72 @@ public final class ChatListNavigationBar: Component { let visibleSize = CGSize(width: currentLayout.size.width, height: max(0.0, currentLayout.size.height - clippedScrollOffset)) - let previousHeight = self.currentHeight + let previousHeight = self.separatorLayer.position.y - /*self.backgroundView.update(size: CGSize(width: visibleSize.width, height: 1000.0), transition: transition.containedViewLayoutTransition) + self.backgroundView.update(size: CGSize(width: visibleSize.width, height: 1000.0), transition: transition.containedViewLayoutTransition) transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: CGPoint(), size: CGSize(width: visibleSize.width, height: 1000.0))) transition.animatePosition(view: self.backgroundView, from: CGPoint(x: 0.0, y: -visibleSize.height + self.backgroundView.layer.position.y), to: CGPoint(), additive: true) - self.backgroundView.layer.position = CGPoint(x: 0.0, y: visibleSize.height)*/ - - let edgeEffectHeight: CGFloat = currentLayout.size.height + 20.0 - var edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: currentLayout.size.width, height: edgeEffectHeight)) - if component.isSearchActive { - edgeEffectFrame.origin.y -= 46.0 - } - transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) - self.edgeEffectView.update(content: component.theme.list.plainBackgroundColor, blur: true, alpha: 0.75, rect: edgeEffectFrame, edge: .top, edgeSize: 40.0, transition: transition) + self.backgroundView.layer.position = CGPoint(x: 0.0, y: visibleSize.height) - self.currentHeight = visibleSize.height + transition.setFrameWithAdditivePosition(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: UIScreenPixel))) - if let search = component.search { - 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 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 = 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) - } - ) - searchContentNode.view.layer.anchorPoint = CGPoint() - self.searchContentNode = searchContentNode - self.addSubview(searchContentNode.view) + searchContentNode.updateThemeAndPlaceholder(theme: component.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder) } - - let searchSize = CGSize(width: currentLayout.size.width - 6.0 * 2.0, height: navigationBarSearchContentHeight) - var searchFrame = CGRect(origin: CGPoint(x: 6.0, y: visibleSize.height - searchSize.height), size: searchSize) - if component.tabsNode != nil { - searchFrame.origin.y -= 40.0 - } - if !component.isSearchActive { - searchFrame.origin.y -= component.accessoryPanelContainerHeight - } - - let clippedSearchOffset = max(0.0, min(clippedScrollOffset, searchOffsetDistance)) - let searchOffsetFraction = clippedSearchOffset / searchOffsetDistance - searchContentNode.expansionProgress = 1.0 - searchOffsetFraction - - transition.setFrameWithAdditivePosition(view: searchContentNode.view, frame: searchFrame) - - searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition) - - transition.setAlpha(view: searchContentNode.view, alpha: search.isEnabled ? 1.0 : 0.5) - searchContentNode.isUserInteractionEnabled = search.isEnabled } else { - if let searchContentNode = self.searchContentNode { - self.searchContentNode = nil - searchContentNode.view.removeFromSuperview() - } + let placeholder: String + let compactPlaceholder: String + + placeholder = component.strings.Common_Search + compactPlaceholder = component.strings.Common_Search + + 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) + } + ) + searchContentNode.view.layer.anchorPoint = CGPoint() + self.searchContentNode = searchContentNode + self.addSubview(searchContentNode.view) } + let searchSize = CGSize(width: currentLayout.size.width - 6.0 * 2.0, height: navigationBarSearchContentHeight) + var searchFrame = CGRect(origin: CGPoint(x: 6.0, y: visibleSize.height - searchSize.height), size: searchSize) + if component.tabsNode != nil { + searchFrame.origin.y -= 40.0 + } + if !component.isSearchActive { + searchFrame.origin.y -= component.accessoryPanelContainerHeight + } + + let clippedSearchOffset = max(0.0, min(clippedScrollOffset, searchOffsetDistance)) + let searchOffsetFraction = clippedSearchOffset / searchOffsetDistance + searchContentNode.expansionProgress = 1.0 - searchOffsetFraction + + transition.setFrameWithAdditivePosition(view: searchContentNode.view, frame: searchFrame) + + searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition) + + transition.setAlpha(view: searchContentNode.view, alpha: component.isSearchEnabled ? 1.0 : 0.5) + searchContentNode.isUserInteractionEnabled = component.isSearchEnabled + let headerTransition = transition let storiesOffsetFraction: CGFloat @@ -481,8 +455,7 @@ public final class ChatListNavigationBar: Component { } var tabsFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: 46.0)) - if component.isSearchActive { - } else { + if !component.isSearchActive { tabsFrame.origin.y -= component.accessoryPanelContainerHeight } if component.tabsNode != nil { @@ -490,8 +463,7 @@ public final class ChatListNavigationBar: Component { } var accessoryPanelContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: component.accessoryPanelContainerHeight)) - if component.isSearchActive { - } else { + if !component.isSearchActive { accessoryPanelContainerFrame.origin.y -= component.accessoryPanelContainerHeight } @@ -556,8 +528,8 @@ public final class ChatListNavigationBar: Component { strings: component.strings, statusBarHeight: component.statusBarHeight, sideInset: component.sideInset, - search: component.search, isSearchActive: component.isSearchActive, + isSearchEnabled: component.isSearchEnabled, primaryContent: component.primaryContent, secondaryContent: component.secondaryContent, secondaryTransition: component.secondaryTransition, @@ -603,6 +575,8 @@ public final class ChatListNavigationBar: Component { } func update(component: ChatListNavigationBar, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme + var uploadProgressUpdated = false var storySubscriptionsUpdated = false if let previousComponent = self.component { @@ -617,20 +591,23 @@ public final class ChatListNavigationBar: Component { 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 += 3.0 } contentHeight += 44.0 - contentHeight += 8.0 if component.isSearchActive { if component.statusBarHeight < 1.0 { contentHeight += 8.0 } - } - if component.search != nil { + } else { contentHeight += navigationBarSearchContentHeight } @@ -638,11 +615,8 @@ public final class ChatListNavigationBar: Component { contentHeight += 40.0 } - if component.isSearchActive { - } else { - if component.accessoryPanelContainer != nil { - contentHeight += component.accessoryPanelContainerHeight - } + if component.accessoryPanelContainer != nil && !component.isSearchActive { + contentHeight += component.accessoryPanelContainerHeight } let size = CGSize(width: availableSize.width, height: contentHeight) diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonComponent.swift deleted file mode 100644 index 4027524c18..0000000000 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonComponent.swift +++ /dev/null @@ -1,254 +0,0 @@ -import Foundation -import UIKit -import ComponentFlow -import ChatListTitleView -import TelegramPresentationData -import Display -import MoreHeaderButton - -public final class NavigationButtonComponentEnvironment: Equatable { - public let theme: PresentationTheme - - public init(theme: PresentationTheme) { - self.theme = theme - } - - public static func ==(lhs: NavigationButtonComponentEnvironment, rhs: NavigationButtonComponentEnvironment) -> Bool { - if lhs.theme != rhs.theme { - return false - } - return true - } -} - -public final class NavigationButtonComponent: Component { - public typealias EnvironmentType = NavigationButtonComponentEnvironment - - public enum Content: Equatable { - case text(title: String, isBold: Bool) - case more - case icon(imageName: String) - case proxy(status: ChatTitleProxyStatus) - } - - public let content: Content - public let pressed: (UIView) -> Void - public let contextAction: ((UIView, ContextGesture?) -> Void)? - - public init( - content: Content, - pressed: @escaping (UIView) -> Void, - contextAction: ((UIView, ContextGesture?) -> Void)? = nil - ) { - self.content = content - self.pressed = pressed - self.contextAction = contextAction - } - - public static func ==(lhs: NavigationButtonComponent, rhs: NavigationButtonComponent) -> Bool { - if lhs.content != rhs.content { - return false - } - return true - } - - public final class View: HighlightTrackingButton { - private var textView: ImmediateTextView? - - private var iconView: UIImageView? - private var iconImageName: String? - - private var proxyNode: ChatTitleProxyNode? - - private var moreButton: MoreHeaderButton? - - private var component: NavigationButtonComponent? - private var theme: PresentationTheme? - - override init(frame: CGRect) { - super.init(frame: frame) - - self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) - - self.highligthedChanged = { [weak self] highlighted in - guard let self else { - return - } - if highlighted { - self.textView?.alpha = 0.6 - self.proxyNode?.alpha = 0.6 - self.iconView?.alpha = 0.6 - } else { - self.textView?.alpha = 1.0 - self.textView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) - - self.proxyNode?.alpha = 1.0 - self.proxyNode?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) - - self.iconView?.alpha = 1.0 - self.iconView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) - } - } - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc private func pressed() { - self.component?.pressed(self) - } - - func update(component: NavigationButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.component = component - - let theme = environment[NavigationButtonComponentEnvironment.self].value.theme - var themeUpdated = false - if self.theme !== theme { - self.theme = theme - themeUpdated = true - } - - var textString: NSAttributedString? - var imageName: String? - var proxyStatus: ChatTitleProxyStatus? - var isMore: Bool = false - - switch component.content { - case let .text(title, isBold): - textString = NSAttributedString(string: title, font: isBold ? Font.bold(17.0) : Font.regular(17.0), textColor: theme.chat.inputPanel.panelControlColor) - case .more: - isMore = true - case let .icon(imageNameValue): - imageName = imageNameValue - case let .proxy(status): - proxyStatus = status - } - - var size = CGSize(width: 0.0, height: availableSize.height) - - if let textString = textString { - let textView: ImmediateTextView - if let current = self.textView { - textView = current - } else { - textView = ImmediateTextView() - textView.isUserInteractionEnabled = false - self.textView = textView - self.addSubview(textView) - } - - textView.attributedText = textString - let textSize = textView.updateLayout(availableSize) - let textInset: CGFloat = 10.0 - size.width = max(44.0, textSize.width + textInset * 2.0) - - textView.frame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((availableSize.height - textSize.height) / 2.0)), size: textSize) - } else if let textView = self.textView { - self.textView = nil - textView.removeFromSuperview() - } - - if let imageName = imageName { - let iconView: UIImageView - if let current = self.iconView { - iconView = current - } else { - iconView = UIImageView() - iconView.isUserInteractionEnabled = false - self.iconView = iconView - self.addSubview(iconView) - } - if self.iconImageName != imageName || themeUpdated { - self.iconImageName = imageName - iconView.image = generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.chat.inputPanel.panelControlColor) - } - - if let iconSize = iconView.image?.size { - size.width = 44.0 - - iconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((availableSize.height - iconSize.height) / 2.0)), size: iconSize) - } - } else if let iconView = self.iconView { - self.iconView = nil - iconView.removeFromSuperview() - self.iconImageName = nil - } - - if let proxyStatus = proxyStatus { - let proxyNode: ChatTitleProxyNode - if let current = self.proxyNode { - proxyNode = current - } else { - proxyNode = ChatTitleProxyNode(theme: theme) - proxyNode.isUserInteractionEnabled = false - self.proxyNode = proxyNode - self.addSubnode(proxyNode) - } - - let proxySize = CGSize(width: 30.0, height: 30.0) - size.width = 44.0 - - proxyNode.theme = theme - proxyNode.status = proxyStatus - - proxyNode.frame = CGRect(origin: CGPoint(x: floor((size.width - proxySize.width) / 2.0), y: floor((availableSize.height - proxySize.height) / 2.0)), size: proxySize) - } else if let proxyNode = self.proxyNode { - self.proxyNode = nil - proxyNode.removeFromSupernode() - } - - if isMore { - let moreButton: MoreHeaderButton - if let current = self.moreButton, !themeUpdated { - moreButton = current - } else { - if let moreButton = self.moreButton { - moreButton.removeFromSupernode() - self.moreButton = nil - } - - moreButton = MoreHeaderButton(color: theme.chat.inputPanel.panelControlColor) - moreButton.isUserInteractionEnabled = true - moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.chat.inputPanel.panelControlColor))) - moreButton.onPressed = { [weak self] in - guard let self, let component = self.component else { - return - } - self.moreButton?.play() - component.pressed(self) - } - moreButton.contextAction = { [weak self] sourceNode, gesture in - guard let self, let component = self.component else { - return - } - self.moreButton?.play() - component.contextAction?(self, gesture) - } - self.moreButton = moreButton - self.addSubnode(moreButton) - } - - let buttonSize = CGSize(width: 30.0, height: 44.0) - size.width = 44.0 - - moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.rootController.navigationBar.buttonColor))) - - moreButton.frame = CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) - } else if let moreButton = self.moreButton { - self.moreButton = nil - moreButton.removeFromSupernode() - } - - return size - } - } - - public func makeView() -> View { - return View(frame: CGRect()) - } - - public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonContainerView.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonContainerView.swift deleted file mode 100644 index bdc1e32e4e..0000000000 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonContainerView.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation -import UIKit -import ComponentFlow - -final class NavigationButtonContainerView: UIView { -} diff --git a/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift b/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift index b3d2606b47..5ec474b50d 100644 --- a/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift +++ b/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift @@ -44,13 +44,13 @@ public final class ChatTitleProxyNode: ASDisplayNode { if self.theme !== oldValue { switch self.status { case .connecting: - self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: false) + self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: false) case .connected: - self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: true, off: false) + self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: true, off: false) case .available: - self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: true) + self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) } - self.activityIndicator.type = .custom(theme.chat.inputPanel.panelControlColor, 10.0, 1.3333, true) + self.activityIndicator.type = .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.3333, true) } } } @@ -61,13 +61,13 @@ public final class ChatTitleProxyNode: ASDisplayNode { switch self.status { case .connecting: self.activityIndicator.isHidden = false - self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: false) + self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: false) case .connected: self.activityIndicator.isHidden = true - self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: true, off: false) + self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: true, off: false) case .available: self.activityIndicator.isHidden = true - self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: true) + self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) } } } @@ -80,9 +80,9 @@ public final class ChatTitleProxyNode: ASDisplayNode { self.iconNode.isLayerBacked = true self.iconNode.displayWithoutProcessing = true self.iconNode.displaysAsynchronously = false - self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: true) + self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) - self.activityIndicator = ActivityIndicator(type: .custom(theme.chat.inputPanel.panelControlColor, 10.0, 1.3333, true), speed: .slow) + self.activityIndicator = ActivityIndicator(type: .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.3333, true), speed: .slow) super.init() diff --git a/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift b/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift index 040181f130..e5d1e84948 100644 --- a/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift +++ b/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift @@ -64,15 +64,13 @@ public final class LiquidLensView: UIView { var selectionWidth: CGFloat var isDark: Bool var isLifted: Bool - var isCollapsed: Bool - init(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool) { + init(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool) { self.size = size self.selectionX = selectionX self.selectionWidth = selectionWidth self.isLifted = isLifted self.isDark = isDark - self.isCollapsed = isCollapsed } } @@ -87,8 +85,8 @@ public final class LiquidLensView: UIView { } private let containerView: UIView - private let backgroundContainer: GlassBackgroundContainerView? - private let genericBackgroundContainer: UIView? + private let backgroundContainerContainer: UIView + private let backgroundContainer: GlassBackgroundContainerView private let backgroundView: GlassBackgroundView private var lensView: UIView? private let liftedContainerView: UIView @@ -119,16 +117,11 @@ public final class LiquidLensView: UIView { return self.params?.selectionWidth } - public init(useBackgroundContainer: Bool = true) { + override public init(frame: CGRect) { self.containerView = UIView() - if useBackgroundContainer { - self.backgroundContainer = GlassBackgroundContainerView() - self.genericBackgroundContainer = nil - } else { - self.backgroundContainer = nil - self.genericBackgroundContainer = UIView() - } + self.backgroundContainerContainer = UIView() + self.backgroundContainer = GlassBackgroundContainerView() self.backgroundView = GlassBackgroundView() @@ -137,16 +130,12 @@ public final class LiquidLensView: UIView { self.restingBackgroundView = RestingBackgroundView() - super.init(frame: CGRect()) + super.init(frame: frame) - if let backgroundContainer = self.backgroundContainer { - self.addSubview(backgroundContainer) - backgroundContainer.contentView.addSubview(self.backgroundView) - } else if let genericBackgroundContainer = self.genericBackgroundContainer { - self.addSubview(genericBackgroundContainer) - genericBackgroundContainer.addSubview(self.backgroundView) - } + self.backgroundContainerContainer.addSubview(self.backgroundContainer) + self.addSubview(self.backgroundContainerContainer) + self.backgroundContainer.contentView.addSubview(self.backgroundView) self.backgroundView.contentView.addSubview(self.containerView) self.containerView.isUserInteractionEnabled = false @@ -161,11 +150,7 @@ public final class LiquidLensView: UIView { } if let lensView = self.lensView { - if let backgroundContainer = self.backgroundContainer { - backgroundContainer.layer.zPosition = 1 - } else if let genericBackgroundContainer = self.genericBackgroundContainer{ - genericBackgroundContainer.layer.zPosition = 1 - } + self.backgroundContainer.layer.zPosition = 1 lensView.layer.zPosition = 10.0 self.liftedContainerView.addSubview(self.restingBackgroundView) @@ -174,11 +159,7 @@ public final class LiquidLensView: UIView { self.containerView.addSubview(lensView) self.containerView.addSubview(self.contentView) - if let backgroundContainer = self.backgroundContainer { - lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: backgroundContainer.contentView) - } else if let genericBackgroundContainer = self.genericBackgroundContainer { - lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: genericBackgroundContainer) - } + lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: self.backgroundContainer.contentView) lensView.perform(NSSelectorFromString("setLiftedContentView:"), with: self.liftedContainerView) lensView.perform(NSSelectorFromString("setOverridePunchoutView:"), with: self.contentView) @@ -242,8 +223,8 @@ public final class LiquidLensView: UIView { fatalError("init(coder:) has not been implemented") } - public func update(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool = false, transition: ComponentTransition) { - let params = Params(size: size, selectionX: selectionX, selectionWidth: selectionWidth, isDark: isDark, isLifted: isLifted, isCollapsed: isCollapsed) + public func update(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool, transition: ComponentTransition) { + let params = Params(size: size, selectionX: selectionX, selectionWidth: selectionWidth, isDark: isDark, isLifted: isLifted) if self.params == params { return } @@ -338,36 +319,16 @@ public final class LiquidLensView: UIView { self.params = params transition.setFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: params.size)) + transition.setFrame(view: self.backgroundContainerContainer, frame: CGRect(origin: CGPoint(), size: params.size)) - if let backgroundContainer = self.backgroundContainer { - transition.setFrame(view: backgroundContainer, frame: CGRect(origin: CGPoint(), size: params.size)) - backgroundContainer.update(size: params.size, isDark: params.isDark, transition: transition) - } else if let genericBackgroundContainer = self.genericBackgroundContainer { - transition.setFrame(view: genericBackgroundContainer, frame: CGRect(origin: CGPoint(), size: params.size)) - } + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: params.size)) + self.backgroundContainer.update(size: params.size, isDark: params.isDark, transition: transition) transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size)) self.backgroundView.update(size: params.size, cornerRadius: params.size.height * 0.5, isDark: params.isDark, tintColor: GlassBackgroundView.TintColor.init(kind: .panel, color: UIColor(white: params.isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) - if self.contentView.bounds.size != params.size { - self.contentView.clipsToBounds = true - transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: params.size), completion: { [weak self] completed in - guard let self, completed else { - return - } - self.contentView.clipsToBounds = false - }) - transition.setCornerRadius(layer: self.contentView.layer, cornerRadius: params.size.height * 0.5) - - self.liftedContainerView.clipsToBounds = true - transition.setFrame(view: self.liftedContainerView, frame: CGRect(origin: CGPoint(), size: params.size), completion: { [weak self] completed in - guard let self, completed else { - return - } - self.liftedContainerView.clipsToBounds = false - }) - transition.setCornerRadius(layer: self.liftedContainerView.layer, cornerRadius: params.size.height * 0.5) - } + transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: params.size)) + transition.setFrame(view: self.liftedContainerView, frame: CGRect(origin: CGPoint(), size: params.size)) let baseLensFrame = CGRect(origin: CGPoint(x: max(0.0, min(params.selectionX, params.size.width - params.selectionWidth)), y: 0.0), size: CGSize(width: params.selectionWidth, height: params.size.height)) self.updateLens(params: LensParams(baseFrame: baseLensFrame, isLifted: params.isLifted), animated: !transition.animation.isImmediate) @@ -393,7 +354,7 @@ public final class LiquidLensView: UIView { transition.setFrame(view: self.restingBackgroundView, frame: CGRect(origin: CGPoint(), size: params.size)) self.restingBackgroundView.update(isDark: params.isDark) - transition.setAlpha(view: self.restingBackgroundView, alpha: (params.isLifted || params.isCollapsed) ? 0.0 : 1.0) + transition.setAlpha(view: self.restingBackgroundView, alpha: params.isLifted ? 0.0 : 1.0) if params.isLifted { if self.liftedDisplayLink == nil { diff --git a/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift b/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift index 5cb901cfde..579bd7b8d9 100644 --- a/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift +++ b/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift @@ -266,7 +266,8 @@ final class MiniAppListScreenComponent: Component { } ))) : nil, rightButtons: rightButtons, - backPressed: isModal ? nil : { [weak self] in + backTitle: isModal ? nil : strings.Common_Back, + backPressed: { [weak self] in guard let self else { return } @@ -285,8 +286,8 @@ final class MiniAppListScreenComponent: Component { strings: strings, statusBarHeight: statusBarHeight, sideInset: insets.left, - search: ChatListNavigationBar.Search(isEnabled: true), isSearchActive: self.isSearchDisplayControllerActive, + isSearchEnabled: true, primaryContent: headerContent, secondaryContent: nil, secondaryTransition: 0.0, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index c05923ba09..ad318df6ff 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -13923,8 +13923,6 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } self._readyProxy.set(.single(true)) }) - - self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) } required init(coder aDecoder: NSCoder) { @@ -14478,7 +14476,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar { self.addSubnode(bottomNavigationBar.additionalContentNode) - /*if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { + if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { if let previousBackButtonArrow = headerView.makeTransitionBackArrowView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { self.previousBackButtonArrow = previousBackButtonArrow self.view.addSubview(previousBackButtonArrow) @@ -14487,7 +14485,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig self.previousBackButton = previousBackButton self.view.addSubview(previousBackButton) } - } else*/ do { + } else { if let previousBackButtonArrow = bottomNavigationBar.makeTransitionBackArrowView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { self.previousBackButtonArrow = previousBackButtonArrow self.view.addSubview(previousBackButtonArrow) @@ -14596,13 +14594,13 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } if let previousBackButton = self.previousBackButton { - /*if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { + if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { if let backButtonTitleView = headerView.backButtonTitleView { let previousBackButtonFrame = backButtonTitleView.convert(backButtonTitleView.bounds, to: bottomNavigationBar.view) previousBackButton.frame = previousBackButtonFrame transition.updateAlpha(layer: previousBackButton.layer, alpha: fraction) } - } else*/ do { + } else { let previousBackButtonFrame = bottomNavigationBar.backButtonNode.view.convert(bottomNavigationBar.backButtonNode.view.bounds, to: bottomNavigationBar.view) previousBackButton.frame = previousBackButtonFrame transition.updateAlpha(layer: previousBackButton.layer, alpha: fraction) diff --git a/submodules/TelegramUI/Components/PeerSelectionController/BUILD b/submodules/TelegramUI/Components/PeerSelectionController/BUILD index f2c10542eb..230b96365d 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/BUILD +++ b/submodules/TelegramUI/Components/PeerSelectionController/BUILD @@ -35,7 +35,6 @@ swift_library( "//submodules/TextFormat", "//submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode", "//submodules/CounterControllerTitleView", - "//submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index 0dc5f6b517..db095daadd 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -10,7 +10,6 @@ import AccountContext import SearchUI import ChatListUI import CounterControllerTitleView -import ChatListFilterTabContainerNode public final class PeerSelectionControllerImpl: ViewController, PeerSelectionController { private let context: AccountContext diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift index 0114593bb9..67c0665476 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift @@ -798,7 +798,8 @@ final class QuickReplySetupScreenComponent: Component { } ))) : nil, rightButtons: rightButtons, - backPressed: isModal ? nil :{ [weak self] in + backTitle: isModal ? nil : strings.Common_Back, + backPressed: { [weak self] in guard let self else { return } @@ -817,8 +818,8 @@ final class QuickReplySetupScreenComponent: Component { strings: strings, statusBarHeight: statusBarHeight, sideInset: insets.left, - search: ChatListNavigationBar.Search(isEnabled: !self.isEditing), isSearchActive: self.isSearchDisplayControllerActive, + isSearchEnabled: !self.isEditing, primaryContent: headerContent, secondaryContent: nil, secondaryTransition: 0.0, diff --git a/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift b/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift index 8aadf7da54..b99ea5e371 100644 --- a/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift @@ -298,7 +298,8 @@ final class PeerSelectionScreenComponent: Component { } ))) : nil, rightButtons: rightButtons, - backPressed: isModal ? nil : { [weak self] in + backTitle: isModal ? nil : strings.Common_Back, + backPressed: { [weak self] in guard let self else { return } @@ -317,8 +318,8 @@ final class PeerSelectionScreenComponent: Component { strings: strings, statusBarHeight: statusBarHeight, sideInset: insets.left, - search: ChatListNavigationBar.Search(isEnabled: true), isSearchActive: self.isSearchDisplayControllerActive, + isSearchEnabled: true, primaryContent: headerContent, secondaryContent: nil, secondaryTransition: 0.0, diff --git a/submodules/TelegramUI/Components/TabBarComponent/BUILD b/submodules/TelegramUI/Components/TabBarComponent/BUILD index e08cc9e22b..bf1656c7b3 100644 --- a/submodules/TelegramUI/Components/TabBarComponent/BUILD +++ b/submodules/TelegramUI/Components/TabBarComponent/BUILD @@ -22,7 +22,6 @@ swift_library( "//submodules/UIKitRuntimeUtils", "//submodules/TelegramUI/Components/LiquidLens", "//submodules/AppBundle", - "//submodules/SearchBarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift index 010a270d59..585e15ed8c 100644 --- a/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift +++ b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift @@ -12,7 +12,6 @@ import BundleIconComponent import TextBadgeComponent import LiquidLens import AppBundle -import SearchBarNode private final class TabSelectionRecognizer: UIGestureRecognizer { private var initialLocation: CGPoint? @@ -70,147 +69,35 @@ private final class TabSelectionRecognizer: UIGestureRecognizer { } } -public final class NavigationSearchView: UIView { - private struct Params: Equatable { - let size: CGSize - let theme: PresentationTheme - let strings: PresentationStrings - let isActive: Bool - - init(size: CGSize, theme: PresentationTheme, strings: PresentationStrings, isActive: Bool) { - self.size = size - self.theme = theme - self.strings = strings - self.isActive = isActive - } - - static func ==(lhs: Params, rhs: Params) -> Bool { - if lhs.size != rhs.size { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.strings !== rhs.strings { - return false - } - if lhs.isActive != rhs.isActive { - return false - } - return true - } - } - - private let action: () -> Void - +public final class TabBarSearchView: UIView { private let backgroundView: GlassBackgroundView - private let iconView: UIImageView - private(set) var searchBarNode: SearchBarNode? - - private var params: Params? + private let iconView: GlassBackgroundView.ContentImageView - public init(action: @escaping () -> Void) { - self.action = action - + override public init(frame: CGRect) { self.backgroundView = GlassBackgroundView() - self.backgroundView.contentView.clipsToBounds = true - self.iconView = UIImageView() + self.iconView = GlassBackgroundView.ContentImageView() - super.init(frame: CGRect()) + super.init(frame: frame) self.addSubview(self.backgroundView) self.backgroundView.contentView.addSubview(self.iconView) - - self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:)))) - } - - @objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.action() - } } required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - public func update(size: CGSize, theme: PresentationTheme, strings: PresentationStrings, isActive: Bool, transition: ComponentTransition) { - let params = Params(size: size, theme: theme, strings: strings, isActive: isActive) - if self.params == params { - return - } - self.params = params - self.update(params: params, transition: transition) - } - - private func update(params: Params, transition: ComponentTransition) { - transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size)) - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) - - self.backgroundView.update(size: params.size, cornerRadius: params.size.height * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: GlassBackgroundView.TintColor.init(kind: .panel, color: UIColor(white: params.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + public func update(size: CGSize, isDark: Bool, tintColor: GlassBackgroundView.TintColor, iconColor: UIColor, transition: ComponentTransition) { + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: isDark, tintColor: tintColor, transition: transition) if self.iconView.image == nil { self.iconView.image = UIImage(bundleImageName: "Navigation/Search")?.withRenderingMode(.alwaysTemplate) } - transition.setTintColor(view: self.iconView, color: params.isActive ? params.theme.rootController.navigationSearchBar.inputIconColor : params.theme.chat.inputPanel.panelControlColor) + self.iconView.tintColor = iconColor if let image = self.iconView.image { - let imageSize: CGSize - let iconFrame: CGRect - if params.isActive { - let iconFraction: CGFloat = 0.8 - imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction) - iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floor((params.size.height - imageSize.height) * 0.5)), size: imageSize) - } else { - iconFrame = CGRect(origin: CGPoint(x: floor((params.size.width - image.size.width) * 0.5), y: floor((params.size.height - image.size.height) * 0.5)), size: image.size) - } - transition.setPosition(view: self.iconView, position: iconFrame.center) - transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) - } - - if params.isActive { - let searchBarNode: SearchBarNode - var searchBarNodeTransition = transition - if let current = self.searchBarNode { - searchBarNode = current - } else { - searchBarNodeTransition = searchBarNodeTransition.withAnimation(.none) - searchBarNode = SearchBarNode( - theme: SearchBarNodeTheme( - background: .clear, - separator: .clear, - inputFill: .clear, - primaryText: params.theme.chat.inputPanel.panelControlColor, - placeholder: params.theme.chat.inputPanel.inputPlaceholderColor, - inputIcon: params.theme.chat.inputPanel.inputControlColor, - inputClear: params.theme.chat.inputPanel.inputControlColor, - accent: params.theme.chat.inputPanel.panelControlAccentColor, - keyboard: params.theme.rootController.keyboardColor - ), - strings: params.strings, - fieldStyle: .inlineNavigation, - icon: .loupe, - forceSeparator: false, - displayBackground: false, - cancelText: nil - ) - searchBarNode.placeholderString = NSAttributedString(string: params.strings.Common_Search, font: Font.regular(17.0), textColor: params.theme.chat.inputPanel.inputPlaceholderColor) - self.searchBarNode = searchBarNode - self.backgroundView.contentView.addSubview(searchBarNode.view) - searchBarNode.view.alpha = 0.0 - } - let searchBarFrame = CGRect(origin: CGPoint(x: 36.0, y: 0.0), size: CGSize(width: params.size.width - 36.0 - 4.0, height: params.size.height)) - transition.setFrame(view: searchBarNode.view, frame: searchBarFrame) - searchBarNode.updateLayout(boundingSize: searchBarFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) - alphaTransition.setAlpha(view: searchBarNode.view, alpha: 1.0) - } else { - if let searchBarNode = self.searchBarNode { - self.searchBarNode = nil - let searchBarNodeView = searchBarNode.view - alphaTransition.setAlpha(view: searchBarNode.view, alpha: 0.0, completion: { [weak searchBarNodeView] _ in - searchBarNodeView?.removeFromSuperview() - }) - } + transition.setFrame(view: self.iconView, frame: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size)) } } } @@ -244,45 +131,20 @@ public final class TabBarComponent: Component { return true } } - - public final class Search: Equatable { - public let isActive: Bool - public let activate: () -> Void - public let deactivate: () -> Void - - public init(isActive: Bool, activate: @escaping () -> Void, deactivate: @escaping () -> Void) { - self.isActive = isActive - self.activate = activate - self.deactivate = deactivate - } - - public static func ==(lhs: Search, rhs: Search) -> Bool { - if lhs.isActive != rhs.isActive { - return false - } - return true - } - } public let theme: PresentationTheme - public let strings: PresentationStrings public let items: [Item] - public let search: Search? public let selectedId: AnyHashable? public let isTablet: Bool public init( theme: PresentationTheme, - strings: PresentationStrings, items: [Item], - search: Search?, selectedId: AnyHashable?, isTablet: Bool ) { self.theme = theme - self.strings = strings self.items = items - self.search = search self.selectedId = selectedId self.isTablet = isTablet } @@ -291,15 +153,9 @@ public final class TabBarComponent: Component { if lhs.theme !== rhs.theme { return false } - if lhs.strings !== rhs.strings { - return false - } if lhs.items != rhs.items { return false } - if lhs.search != rhs.search { - return false - } if lhs.selectedId != rhs.selectedId { return false } @@ -310,14 +166,11 @@ public final class TabBarComponent: Component { } public final class View: UIView, UITabBarDelegate, UIGestureRecognizerDelegate { - private let backgroundContainer: GlassBackgroundContainerView private let liquidLensView: LiquidLensView private let contextGestureContainerView: ContextControllerSourceView private var itemViews: [AnyHashable: ComponentView] = [:] private var selectedItemViews: [AnyHashable: ComponentView] = [:] - - private var searchView: NavigationSearchView? private var tabSelectionRecognizer: TabSelectionRecognizer? private var itemWithActiveContextGesture: AnyHashable? @@ -325,16 +178,11 @@ public final class TabBarComponent: Component { private var component: TabBarComponent? private weak var state: EmptyComponentState? - private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable)? + private var selectionGestureState: (startX: CGFloat, currentX: CGFloat)? private var overrideSelectedItemId: AnyHashable? - - public var currentSearchNode: ASDisplayNode? { - return self.searchView?.searchBarNode - } public override init(frame: CGRect) { - self.backgroundContainer = GlassBackgroundContainerView() - self.liquidLensView = LiquidLensView(useBackgroundContainer: false) + self.liquidLensView = LiquidLensView() self.contextGestureContainerView = ContextControllerSourceView() self.contextGestureContainerView.isGestureEnabled = true @@ -346,13 +194,12 @@ public final class TabBarComponent: Component { self.traitOverrides.horizontalSizeClass = .compact } - self.addSubview(self.backgroundContainer) - self.backgroundContainer.contentView.addSubview(self.contextGestureContainerView) + self.addSubview(self.contextGestureContainerView) self.contextGestureContainerView.addSubview(self.liquidLensView) let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:))) self.tabSelectionRecognizer = tabSelectionRecognizer - self.contextGestureContainerView.addGestureRecognizer(tabSelectionRecognizer) + self.addGestureRecognizer(tabSelectionRecognizer) self.contextGestureContainerView.shouldBegin = { [weak self] point in guard let self, let component = self.component else { @@ -434,41 +281,29 @@ public final class TabBarComponent: Component { } @objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) { - guard let component = self.component else { - return - } switch recognizer.state { case .began: - if let search = component.search, search.isActive { - } else if let itemId = self.item(at: recognizer.location(in: self)), let itemView = self.itemViews[itemId]?.view { + if let itemId = self.item(at: recognizer.location(in: self)), let itemView = self.itemViews[itemId]?.view { let startX = itemView.frame.minX - 4.0 - self.selectionGestureState = (startX, startX, itemId) + self.selectionGestureState = (startX, startX) self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) } case .changed: - if let search = component.search, search.isActive { - } else if var selectionGestureState = self.selectionGestureState { + if var selectionGestureState = self.selectionGestureState { selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self).x - if let itemId = self.item(at: recognizer.location(in: self)) { - selectionGestureState.itemId = itemId - } self.selectionGestureState = selectionGestureState self.state?.updated(transition: .immediate, isLocal: true) } case .ended, .cancelled: - if let search = component.search, search.isActive { - search.deactivate() - } else if let selectionGestureState = self.selectionGestureState { - self.selectionGestureState = nil - if case .ended = recognizer.state, let component = self.component { - guard let item = component.items.first(where: { $0.id == selectionGestureState.itemId }) else { - return - } - self.overrideSelectedItemId = selectionGestureState.itemId - item.action(false) + self.selectionGestureState = nil + if let component = self.component, let itemId = self.item(at: recognizer.location(in: self)) { + guard let item = component.items.first(where: { $0.id == itemId }) else { + return } - self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) + self.overrideSelectedItemId = itemId + item.action(false) } + self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) default: break } @@ -520,28 +355,22 @@ public final class TabBarComponent: Component { } func update(component: TabBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) - let _ = alphaTransition - let innerInset: CGFloat = 4.0 let availableSize = CGSize(width: min(500.0, availableSize.width), height: availableSize.height) let previousComponent = self.component self.component = component self.state = state + + let _ = innerInset + let _ = availableSize + let _ = previousComponent self.overrideUserInterfaceStyle = component.theme.overallDarkAppearance ? .dark : .light - let barHeight: CGFloat = 56.0 + innerInset * 2.0 - - var availableItemsWidth: CGFloat = availableSize.width - innerInset * 2.0 - if component.search != nil { - availableItemsWidth -= barHeight + 8.0 - } - - let itemSize = CGSize(width: floor(availableItemsWidth / CGFloat(component.items.count)), height: 56.0) + let itemSize = CGSize(width: floor((availableSize.width - innerInset * 2.0) / CGFloat(component.items.count)), height: 56.0) let contentWidth: CGFloat = innerInset * 2.0 + CGFloat(component.items.count) * itemSize.width - let tabsSize = CGSize(width: min(availableSize.width, contentWidth), height: itemSize.height + innerInset * 2.0) + let size = CGSize(width: min(availableSize.width, contentWidth), height: itemSize.height + innerInset * 2.0) var validIds: [AnyHashable] = [] var selectionFrame: CGRect? @@ -580,7 +409,6 @@ public final class TabBarComponent: Component { component: AnyComponent(ItemComponent( item: item, theme: component.theme, - isCompact: component.search?.isActive == true, isSelected: false )), environment: {}, @@ -591,21 +419,14 @@ public final class TabBarComponent: Component { component: AnyComponent(ItemComponent( item: item, theme: component.theme, - isCompact: component.search?.isActive == true, isSelected: true )), environment: {}, containerSize: itemSize ) - var itemFrame = CGRect(origin: CGPoint(x: innerInset + CGFloat(index) * itemSize.width, y: floor((tabsSize.height - itemSize.height) * 0.5)), size: itemSize) - if isItemSelected { - selectionFrame = itemFrame - } - + let itemFrame = CGRect(origin: CGPoint(x: innerInset + CGFloat(index) * itemSize.width, y: floor((size.height - itemSize.height) * 0.5)), size: itemSize) if let itemComponentView = itemView.view as? ItemComponent.View, let selectedItemComponentView = selectedItemView.view as? ItemComponent.View { - let itemAlphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) - if itemComponentView.superview == nil { itemComponentView.isUserInteractionEnabled = false selectedItemComponentView.isUserInteractionEnabled = false @@ -613,27 +434,6 @@ public final class TabBarComponent: Component { self.liquidLensView.contentView.addSubview(itemComponentView) self.liquidLensView.selectedContentView.addSubview(selectedItemComponentView) } - - if let search = component.search, search.isActive { - if isItemSelected { - itemFrame.origin.x = floor((48.0 - itemSize.width) * 0.5) - itemTransition.setAlpha(view: itemComponentView, alpha: 1.0) - itemAlphaTransition.setBlur(layer: itemComponentView.layer, radius: 0.0) - itemTransition.setAlpha(view: selectedItemComponentView, alpha: 1.0) - itemAlphaTransition.setBlur(layer: selectedItemComponentView.layer, radius: 0.0) - } else { - itemTransition.setAlpha(view: itemComponentView, alpha: 0.0) - itemAlphaTransition.setBlur(layer: itemComponentView.layer, radius: 10.0) - itemTransition.setAlpha(view: selectedItemComponentView, alpha: 0.0) - itemAlphaTransition.setBlur(layer: selectedItemComponentView.layer, radius: 10.0) - } - } else { - itemTransition.setAlpha(view: itemComponentView, alpha: 1.0) - itemAlphaTransition.setBlur(layer: itemComponentView.layer, radius: 0.0) - itemTransition.setAlpha(view: selectedItemComponentView, alpha: 1.0) - itemAlphaTransition.setBlur(layer: selectedItemComponentView.layer, radius: 0.0) - } - itemTransition.setFrame(view: itemComponentView, frame: itemFrame) itemTransition.setPosition(view: selectedItemComponentView, position: itemFrame.center) itemTransition.setBounds(view: selectedItemComponentView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) @@ -644,6 +444,9 @@ public final class TabBarComponent: Component { selectedItemComponentView.playSelectionAnimation() } } + if isItemSelected { + selectionFrame = itemFrame + } } var removeIds: [AnyHashable] = [] @@ -658,17 +461,12 @@ public final class TabBarComponent: Component { self.itemViews.removeValue(forKey: id) self.selectedItemViews.removeValue(forKey: id) } - - var tabsFrame = CGRect(origin: CGPoint(), size: tabsSize) - if let search = component.search, search.isActive { - tabsFrame.size = CGSize(width: 48.0, height: 48.0) - tabsFrame.origin.y = tabsSize.height - 48.0 - } - transition.setFrame(view: self.contextGestureContainerView, frame: tabsFrame) - transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(), size: tabsSize)) + transition.setFrame(view: self.contextGestureContainerView, frame: CGRect(origin: CGPoint(), size: size)) + + transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(), size: size)) - var lensSelection: (x: CGFloat, width: CGFloat) + let lensSelection: (x: CGFloat, width: CGFloat) if let selectionGestureState = self.selectionGestureState { lensSelection = (selectionGestureState.currentX, itemSize.width + innerInset * 2.0) } else if let selectionFrame { @@ -677,62 +475,7 @@ public final class TabBarComponent: Component { lensSelection = (0.0, itemSize.width) } - var lensSize: CGSize = tabsSize - var isLensCollapsed = false - if let search = component.search, search.isActive { - isLensCollapsed = true - lensSize = CGSize(width: 48.0, height: 48.0) - lensSelection = (0.0, 48.0) - } - self.liquidLensView.update(size: lensSize, selectionX: lensSelection.x, selectionWidth: lensSelection.width, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: isLensCollapsed, transition: transition) - - var size = tabsSize - - if let search = component.search { - let searchSize: CGSize - let searchFrame: CGRect - if search.isActive { - size.width = availableSize.width - searchSize = CGSize(width: availableSize.width - 48.0 - 8.0, height: 48.0) - searchFrame = CGRect(origin: CGPoint(x: 48.0 + 8.0, y: size.height - searchSize.height), size: searchSize) - } else { - searchSize = CGSize(width: barHeight, height: barHeight) - size.width += barHeight + 8.0 - searchFrame = CGRect(origin: CGPoint(x: availableSize.width - searchSize.width, y: 0.0), size: searchSize) - } - - let searchView: NavigationSearchView - var searchViewTransition = transition - if let current = self.searchView { - searchView = current - } else { - searchViewTransition = searchViewTransition.withAnimation(.none) - searchView = NavigationSearchView(action: { [weak self] in - guard let self, let component = self.component else { - return - } - component.search?.activate() - }) - self.searchView = searchView - self.backgroundContainer.contentView.addSubview(searchView) - searchView.frame = CGRect(origin: CGPoint(x: availableSize.width + 50.0, y: 0.0), size: searchSize) - } - searchView.update(size: searchSize, theme: component.theme, strings: component.strings, isActive: search.isActive, transition: searchViewTransition) - transition.setFrame(view: searchView, frame: searchFrame) - } else { - if let searchView = self.searchView { - self.searchView = nil - transition.setFrame(view: searchView, frame: CGRect(origin: CGPoint(x: availableSize.width + 50.0, y: 0.0), size: searchView.bounds.size), completion: { [weak searchView] completed in - guard let searchView, completed else { - return - } - searchView.removeFromSuperview() - }) - } - } - - transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) - self.backgroundContainer.update(size: size, isDark: component.theme.overallDarkAppearance, transition: transition) + self.liquidLensView.update(size: size, selectionX: lensSelection.x, selectionWidth: lensSelection.width, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, transition: transition) return size } @@ -750,13 +493,11 @@ public final class TabBarComponent: Component { private final class ItemComponent: Component { let item: TabBarComponent.Item let theme: PresentationTheme - let isCompact: Bool let isSelected: Bool - init(item: TabBarComponent.Item, theme: PresentationTheme, isCompact: Bool, isSelected: Bool) { + init(item: TabBarComponent.Item, theme: PresentationTheme, isSelected: Bool) { self.item = item self.theme = theme - self.isCompact = isCompact self.isSelected = isSelected } @@ -767,9 +508,6 @@ private final class ItemComponent: Component { if lhs.theme !== rhs.theme { return false } - if lhs.isCompact != rhs.isCompact { - return false - } if lhs.isSelected != rhs.isSelected { return false } @@ -824,8 +562,6 @@ private final class ItemComponent: Component { } func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) - let previousComponent = self.component if previousComponent?.item.item !== component.item.item { @@ -883,7 +619,7 @@ private final class ItemComponent: Component { content: LottieComponent.AppBundleContent( name: animationName ), - color: (component.isSelected && !component.isCompact) ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor, + color: component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor, placeholderColor: nil, startingPosition: .end, size: CGSize(width: 48.0, height: 48.0), @@ -956,7 +692,6 @@ private final class ItemComponent: Component { self.contextContainerView.contentView.addSubview(titleView) } titleView.frame = titleFrame - alphaTransition.setAlpha(view: titleView, alpha: component.isCompact ? 0.0 : 1.0) } if let badgeText = component.item.item.badgeValue, !badgeText.isEmpty { @@ -988,7 +723,6 @@ private final class ItemComponent: Component { self.contextContainerView.contentView.addSubview(badgeView) } badgeTransition.setFrame(view: badgeView, frame: badgeFrame) - alphaTransition.setAlpha(view: badgeView, alpha: component.isCompact ? 0.0 : 1.0) } } else if let badge = self.badge { self.badge = nil diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 2abea859eb..c0f1427eee 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -188,7 +188,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } public func addRootControllers(showCallsTab: Bool) { - let tabBarController = TabBarControllerImpl(theme: self.presentationData.theme, strings: self.presentationData.strings) + let tabBarController = TabBarControllerImpl(theme: self.presentationData.theme) tabBarController.navigationPresentation = .master let chatListController = self.context.sharedContext.makeChatListController(context: self.context, location: .chatList(groupId: .root), controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild) if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {