Search filters improvements

This commit is contained in:
Ilya Laktyushin 2020-09-13 08:15:22 +03:00
parent 648483db46
commit da9cdbf433
9 changed files with 112 additions and 44 deletions

View File

@ -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)

View File

@ -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

View File

@ -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))
}
}

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_search_channel.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_search_group.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}