diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 3c596ca0b3..bfd25cf94f 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1683,10 +1683,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } if let searchContentNode = strongSelf.searchContentNode { - var updatedSearchOptionsImpl: ((ChatListSearchOptions?, Bool) -> Void)? + var updatedDisplayFiltersPanelImpl: ((Bool) -> Void)? - if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, navigationController: strongSelf.navigationController as? NavigationController, updatedSearchOptions: { options, hasDate in - updatedSearchOptionsImpl?(options, hasDate) + if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, navigationController: strongSelf.navigationController as? NavigationController, updatedDisplayFiltersPanel: { display in + updatedDisplayFiltersPanelImpl?(display) }) { let (filterContainerNode, activate) = filterContainerNodeAndActivate strongSelf.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: false) @@ -1695,20 +1695,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } activate() - var currentHasSuggestions = true - updatedSearchOptionsImpl = { [weak self, weak filterContainerNode] options, hasSuggestions in + var currentDisplay = true + updatedDisplayFiltersPanelImpl = { [weak self, weak filterContainerNode] display in guard let strongSelf = self, let strongFilterContainerNode = filterContainerNode else { return } - if currentHasSuggestions != hasSuggestions { - currentHasSuggestions = hasSuggestions + if currentDisplay != display { + currentDisplay = display - var node: ASDisplayNode? - if let options = options, options.messageTags != nil && !hasSuggestions { - } else { - node = strongFilterContainerNode - } - + let node = display ? strongFilterContainerNode : nil strongSelf.navigationBar?.setSecondaryContentNode(node, animated: false) if let parentController = strongSelf.parent as? TabBarController { parentController.navigationBar?.setSecondaryContentNode(node, animated: true) diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index f66e5f0318..a700fbc752 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -1147,7 +1147,7 @@ final class ChatListControllerNode: ASDisplayNode { } } - func activateSearch(placeholderNode: SearchBarPlaceholderNode, navigationController: NavigationController?, updatedSearchOptions: ((ChatListSearchOptions?, Bool) -> Void)?) -> (ASDisplayNode, () -> Void)? { + func activateSearch(placeholderNode: SearchBarPlaceholderNode, navigationController: NavigationController?, updatedDisplayFiltersPanel: ((Bool) -> Void)?) -> (ASDisplayNode, () -> Void)? { guard let (containerLayout, _, _, cleanNavigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else { return nil } @@ -1169,8 +1169,8 @@ final class ChatListControllerNode: ASDisplayNode { self?.controller?.present(c, in: .window(.root), with: a) }, presentInGlobalOverlay: { [weak self] c, a in self?.controller?.presentInGlobalOverlay(c, with: a) - }, navigationController: navigationController, updatedSearchOptions: { options, hasDate in - updatedSearchOptions?(options, hasDate) + }, navigationController: navigationController, updatedDisplayFiltersPanel: { display in + updatedDisplayFiltersPanel?(display) }) self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: contentNode, cancel: { [weak self] in diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 0a5714d4d3..5b3f0459cc 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -657,13 +657,17 @@ public enum ChatListSearchContextActionSource { } public struct ChatListSearchOptions { - let peer: (PeerId, String)? + let peer: (PeerId, Bool, String)? let minDate: (Int32, String)? let maxDate: (Int32, String)? let messageTags: MessageTags? - func withUpdatedPeer(_ peerIdAndName: (PeerId, String)?) -> ChatListSearchOptions { - return ChatListSearchOptions(peer: peerIdAndName, minDate: self.minDate, maxDate: self.maxDate, messageTags: self.messageTags) + var isEmpty: Bool { + return self.peer == nil && self.minDate == nil && self.maxDate == nil && self.messageTags == nil + } + + func withUpdatedPeer(_ peerIdIsGroupAndName: (PeerId, Bool, String)?) -> ChatListSearchOptions { + return ChatListSearchOptions(peer: peerIdIsGroupAndName, minDate: self.minDate, maxDate: self.maxDate, messageTags: self.messageTags) } func withUpdatedMinDate(_ minDateAndTitle: (Int32, String)?) -> ChatListSearchOptions { @@ -734,19 +738,19 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private var mediaAccessoryPanel: (MediaNavigationAccessoryPanel, MediaManagerPlayerType)? private var dismissingPanel: ASDisplayNode? - private let updatedSearchOptions: ((ChatListSearchOptions?, Bool) -> Void)? + private let updatedDisplayFiltersPanel: ((Bool) -> Void)? private let emptyResultsTitleNode: ImmediateTextNode private let emptyResultsTextNode: ImmediateTextNode private let emptyResultsAnimationNode: AnimatedStickerNode private var animationSize: CGSize = CGSize() - public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?, updatedSearchOptions: ((ChatListSearchOptions?, Bool) -> Void)? = nil) { + public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?, updatedDisplayFiltersPanel: ((Bool) -> Void)? = nil) { self.context = context self.peersFilter = filter self.dimNode = ASDisplayNode() self.navigationController = navigationController - self.updatedSearchOptions = updatedSearchOptions + self.updatedDisplayFiltersPanel = updatedDisplayFiltersPanel self.present = present self.presentInGlobalOverlay = presentInGlobalOverlay @@ -910,7 +914,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } let location: SearchMessagesLocation if let options = options { - if let (peerId, _) = options.peer { + if let (peerId, _, _) = options.peer { location = .peer(peerId: peerId, fromId: nil, tags: options.messageTags, topMsgId: nil, minDate: options.minDate?.0, maxDate: options.maxDate?.0) } else { @@ -1555,7 +1559,15 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo guard let strongSelf = self else { return } - strongSelf.updateSearchOptions(strongSelf.currentSearchOptions.withUpdatedPeer((peer.id, peer.compactDisplayTitle)), clearQuery: true) + let isGroup: Bool + if let channel = peer as? TelegramChannel, case .group = channel.info { + isGroup = true + } else if peer.id.namespace == Namespaces.Peer.CloudGroup { + isGroup = true + } else { + isGroup = false + } + strongSelf.updateSearchOptions(strongSelf.currentSearchOptions.withUpdatedPeer((peer.id, isGroup, peer.compactDisplayTitle)), clearQuery: true) strongSelf.dismissInput?() }, searchResults: newEntries.compactMap { entry -> Message? in if case let .message(message, _, _, _, _, _, _) = entry { @@ -1574,7 +1586,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo let previousPossiblePeers = strongSelf.possiblePeers strongSelf.possiblePeers = Array(peers.prefix(10)) - strongSelf.updatedSearchOptions?(strongSelf.searchOptionsValue, strongSelf.hasSuggestions) + strongSelf.updatedDisplayFiltersPanel?(strongSelf.searchOptionsValue?.messageTags == nil || strongSelf.hasSuggestions) if let (layout, navigationBarHeight) = strongSelf.validLayout { strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } @@ -1639,8 +1651,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo case let .date(date, title): maxDate = (date, title) clearQuery = true - case let .peer(id, name): - peer = (id, name) + case let .peer(id, isGroup, _, compactDisplayTitle): + peer = (id, isGroup, compactDisplayTitle) clearQuery = true } strongSelf.updateSearchOptions(strongSelf.currentSearchOptions.withUpdatedMessageTags(messageTags).withUpdatedMaxDate(maxDate).withUpdatedPeer(peer), clearQuery: clearQuery) @@ -1735,6 +1747,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } private func updateSearchOptions(_ options: ChatListSearchOptions?, clearQuery: Bool = false) { + var options = options + if options?.isEmpty ?? true { + options = nil + } self.searchOptionsValue = options self.searchOptions.set(.single(options)) @@ -1764,8 +1780,16 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } } - if let (_, peerName) = options?.peer { - tokens.append(SearchBarToken(id: ChatListTokenId.peer.rawValue, icon: UIImage(bundleImageName: "Chat List/Search/User"), title: peerName)) + if let (peerId, isGroup, peerName) = options?.peer { + let image: UIImage? + if isGroup { + image = UIImage(bundleImageName: "Chat List/Search/Group") + } else if peerId.namespace == Namespaces.Peer.CloudChannel { + image = UIImage(bundleImageName: "Chat List/Search/Channel") + } else { + image = UIImage(bundleImageName: "Chat List/Search/User") + } + tokens.append(SearchBarToken(id: ChatListTokenId.peer.rawValue, icon:image, title: peerName)) } if let (_, dateTitle) = options?.maxDate { @@ -1780,7 +1804,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.setQuery?(nil, tokens, self.searchQueryValue ?? "") } - self.updatedSearchOptions?(options, self.hasSuggestions) + self.updatedDisplayFiltersPanel?(options?.messageTags == nil || self.hasSuggestions) } private func updateTheme(theme: PresentationTheme) { @@ -1832,7 +1856,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.possibleDates = suggestDates(for: text, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat) if previousPossibleDate.isEmpty != self.possibleDates.isEmpty { - self.updatedSearchOptions?(self.searchOptionsValue, self.hasSuggestions) + self.updatedDisplayFiltersPanel?(self.searchOptionsValue?.messageTags == nil || self.hasSuggestions) if let (layout, navigationBarHeight) = self.validLayout { self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } @@ -2202,7 +2226,15 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } if !self.possiblePeers.isEmpty && self.searchOptionsValue?.peer == nil { for peer in self.possiblePeers { - customFilters.append(.peer(peer.id, peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder))) + let isGroup: Bool + if let channel = peer as? TelegramChannel, case .group = channel.info { + isGroup = true + } else if peer.id.namespace == Namespaces.Peer.CloudGroup { + isGroup = true + } else { + isGroup = false + } + customFilters.append(.peer(peer.id, isGroup, peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), peer.compactDisplayTitle)) } } diff --git a/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift index 0023b47b7b..3063bf34ce 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchFiltersContainerNode.swift @@ -13,7 +13,7 @@ enum ChatListSearchFilter: Equatable { case files case music case voice - case peer(PeerId, String) + case peer(PeerId, Bool, String, String) case date(Int32, String) var id: Int32 { @@ -28,7 +28,7 @@ enum ChatListSearchFilter: Equatable { return 3 case .voice: return 4 - case let .peer(peerId, _): + case let .peer(peerId, _, _, _): return peerId.id case let .date(date, _): return date @@ -116,9 +116,17 @@ private final class ItemNode: ASDisplayNode { case .voice: title = presentationData.strings.ChatList_Search_FilterVoice icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Voice"), color: color) - case let .peer(_, displayTitle): + case let .peer(peerId, isGroup, displayTitle, _): title = displayTitle - icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/User"), color: color) + let image: UIImage? + if isGroup { + image = UIImage(bundleImageName: "Chat List/Search/Group") + } else if peerId.namespace == Namespaces.Peer.CloudChannel { + image = UIImage(bundleImageName: "Chat List/Search/Channel") + } else { + image = UIImage(bundleImageName: "Chat List/Search/User") + } + icon = generateTintedImage(image: image, color: color) case let .date(_, displayTitle): title = displayTitle icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Calendar"), color: color) diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 139d3a74b7..b6900adf64 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -168,7 +168,7 @@ private final class TokenNode: ASDisplayNode { } private class SearchBarTextField: UITextField { - public var didDeleteBackwardWhileEmpty: (() -> Void)? + public var didDeleteBackward: (() -> Bool)? let placeholderLabel: ImmediateTextNode var placeholderString: NSAttributedString? { @@ -228,6 +228,7 @@ private class SearchBarTextField: UITextField { var theme: SearchBarNodeTheme fileprivate func layoutTokens(transition: ContainedViewLayoutTransition = .immediate) { + var hasSelected = false for i in 0 ..< self.tokens.count { let token = self.tokens[i] @@ -243,7 +244,10 @@ private class SearchBarTextField: UITextField { self?.becomeFirstResponder() } let isSelected = i == self.selectedTokenIndex - let isCollapsed = !isSelected && (i < self.tokens.count - 1 || !(self.text?.isEmpty ?? true)) + if i < self.tokens.count - 1 && isSelected { + hasSelected = true + } + let isCollapsed = !isSelected && (i < self.tokens.count - 1 || hasSelected) tokenNode.update(theme: self.theme, token: token, isSelected: isSelected, isCollapsed: isCollapsed) } var removeKeys: [AnyHashable] = [] @@ -450,10 +454,12 @@ private class SearchBarTextField: UITextField { } } - if !processed && (self.text == nil || self.text!.isEmpty) { - self.didDeleteBackwardWhileEmpty?() + if !processed { + processed = self.didDeleteBackward?() ?? false + } + if !processed { + super.deleteBackward() } - super.deleteBackward() } } @@ -718,16 +724,19 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.textField.delegate = self self.textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged) - self.textField.didDeleteBackwardWhileEmpty = { [weak self] in + self.textField.didDeleteBackward = { [weak self] in guard let strongSelf = self else { - return + return false } if let index = strongSelf.textField.selectedTokenIndex { strongSelf.tokens.remove(at: index) strongSelf.tokensUpdated?(strongSelf.tokens) - } else { + return true + } else if strongSelf.text.isEmpty { strongSelf.clearPressed() + return true } + return false } self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/Contents.json new file mode 100644 index 0000000000..666f3c4bdc --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_search_channel.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/ic_search_channel.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/ic_search_channel.pdf new file mode 100644 index 0000000000..b44f5524a8 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/ic_search_channel.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/Contents.json new file mode 100644 index 0000000000..9c53dcea52 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_search_group.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/ic_search_group.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/ic_search_group.pdf new file mode 100644 index 0000000000..1dcc97eb8e Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/ic_search_group.pdf differ