mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Search filters improvements
This commit is contained in:
parent
648483db46
commit
da9cdbf433
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_search_channel.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/ic_search_channel.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Search/Channel.imageset/ic_search_channel.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_search_group.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/ic_search_group.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat List/Search/Group.imageset/ic_search_group.pdf
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user