mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 11:23:48 +00:00
Search filters fixes
This commit is contained in:
parent
bf1489eef8
commit
8e22244eab
@ -1682,12 +1682,19 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
scrollToTop()
|
||||
}
|
||||
|
||||
var displaySearchFilters = true
|
||||
if strongSelf.chatListDisplayNode.containerNode.mainItemNode.entriesCount < 10 {
|
||||
displaySearchFilters = false
|
||||
}
|
||||
|
||||
if let searchContentNode = strongSelf.searchContentNode {
|
||||
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, navigationController: strongSelf.navigationController as? NavigationController) {
|
||||
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, navigationController: strongSelf.navigationController as? NavigationController) {
|
||||
let (filterContainerNode, activate) = filterContainerNodeAndActivate
|
||||
strongSelf.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: false)
|
||||
if let parentController = strongSelf.parent as? TabBarController {
|
||||
parentController.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: true)
|
||||
if displaySearchFilters {
|
||||
strongSelf.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: false)
|
||||
if let parentController = strongSelf.parent as? TabBarController {
|
||||
parentController.navigationBar?.setSecondaryContentNode(filterContainerNode, animated: true)
|
||||
}
|
||||
}
|
||||
activate()
|
||||
}
|
||||
|
@ -431,6 +431,10 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
return self.currentItemNodeValue!.listNode
|
||||
}
|
||||
|
||||
var mainItemNode: ChatListNode {
|
||||
return self.itemNodes[.all]!.listNode
|
||||
}
|
||||
|
||||
private let currentItemStateValue = Promise<(state: ChatListNodeState, filterId: Int32?)>()
|
||||
var currentItemState: Signal<(state: ChatListNodeState, filterId: Int32?), NoError> {
|
||||
return self.currentItemStateValue.get()
|
||||
@ -1146,7 +1150,7 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode, navigationController: NavigationController?) -> (ASDisplayNode, () -> Void)? {
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, navigationController: NavigationController?) -> (ASDisplayNode, () -> Void)? {
|
||||
guard let (containerLayout, _, _, cleanNavigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else {
|
||||
return nil
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
case let .message(message, peer, readState, presentationData, totalCount, selected, displayCustomHeader):
|
||||
let header = ChatListSearchItemHeader(type: .messages(totalCount), theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
|
||||
let selection: ChatHistoryMessageSelection = selected.flatMap { .selectable(selected: $0) } ?? .none
|
||||
if let tagMask = tagMask, tagMask != .photoOrVideo && (searchQuery?.isEmpty ?? true) {
|
||||
if let tagMask = tagMask, tagMask != .photoOrVideo {
|
||||
return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings())), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peer.peerId), interaction: listInteraction, message: message, selection: selection, displayHeader: enableHeaders && !displayCustomHeader, customHeader: nil, hintIsLink: tagMask == .webPage, isGlobalSearchResult: true)
|
||||
} else {
|
||||
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
@ -779,9 +779,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
self.addSubnode(self.recentListNode)
|
||||
self.addSubnode(self.listNode)
|
||||
self.addSubnode(self.mediaNode)
|
||||
if key != .chats {
|
||||
self.addSubnode(self.shimmerNode)
|
||||
}
|
||||
self.addSubnode(self.shimmerNode)
|
||||
self.addSubnode(self.mediaAccessoryPanelContainer)
|
||||
|
||||
self.addSubnode(self.emptyResultsAnimationNode)
|
||||
@ -1285,8 +1283,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let previousSelectedMessages = Atomic<Set<MessageId>?>(value: nil)
|
||||
|
||||
let _ = (searchQuery
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak chatListInteraction] query in
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak listInteraction, weak chatListInteraction] query in
|
||||
self?.searchQueryValue = query
|
||||
listInteraction?.searchTextHighightState = query
|
||||
chatListInteraction?.searchTextHighightState = query
|
||||
})
|
||||
|
||||
@ -1817,7 +1816,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let insets = UIEdgeInsets(top: topPanelHeight, left: sideInset, bottom: bottomInset, right: sideInset)
|
||||
|
||||
self.shimmerNode.frame = CGRect(origin: CGPoint(x: overflowInset, y: topInset), size: CGSize(width: size.width - overflowInset * 2.0, height: size.height))
|
||||
self.shimmerNode.update(context: self.context, size: CGSize(width: size.width - overflowInset * 2.0, height: size.height), presentationData: self.presentationData, key: (self.searchQueryValue?.isEmpty ?? true) ? self.key : .chats, transition: transition)
|
||||
self.shimmerNode.update(context: self.context, size: CGSize(width: size.width - overflowInset * 2.0, height: size.height), presentationData: self.presentationData, key: !(self.searchQueryValue?.isEmpty ?? true) && self.key == .media ? .chats : self.key, transition: transition)
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
self.recentListNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
@ -1995,7 +1994,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
strongSelf.emptyResultsTextNode.isHidden = !emptyResults
|
||||
strongSelf.emptyResultsAnimationNode.visibility = emptyResults
|
||||
|
||||
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateAlpha(node: strongSelf.shimmerNode, alpha: transition.isLoading ? 1.0 : 0.0)
|
||||
let displayPlaceholder = transition.isLoading && (strongSelf.key != .chats || strongSelf.searchOptionsValue?.peer != nil || strongSelf.searchOptionsValue?.minDate != nil || strongSelf.searchOptionsValue?.maxDate != nil)
|
||||
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateAlpha(node: strongSelf.shimmerNode, alpha: displayPlaceholder ? 1.0 : 0.0)
|
||||
|
||||
strongSelf.recentListNode.isHidden = displayingResults || strongSelf.peersFilter.contains(.excludeRecent)
|
||||
// strongSelf.dimNode.isHidden = displayingResults
|
||||
|
@ -83,8 +83,7 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
let dd = try NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
|
||||
if let match = dd.firstMatch(in: string, options: [], range: NSMakeRange(0, string.utf16.count)), let date = match.date, date > telegramReleaseDate {
|
||||
func process(_ date: Date) {
|
||||
var resultDate = date
|
||||
if resultDate > now && !calendar.isDate(resultDate, equalTo: now, toGranularity: .year) {
|
||||
if let date = calendar.date(byAdding: .year, value: -1, to: resultDate) {
|
||||
@ -103,6 +102,12 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
|
||||
result.append((resultDate, nil))
|
||||
}
|
||||
}
|
||||
let dd = try NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
|
||||
if let match = dd.firstMatch(in: string, options: [], range: NSMakeRange(0, string.utf16.count)), let date = match.date, date > telegramReleaseDate {
|
||||
process(date)
|
||||
} else if let match = dd.firstMatch(in: string.replacingOccurrences(of: ".", with: "/"), options: [], range: NSMakeRange(0, string.utf16.count)), let date = match.date, date > telegramReleaseDate {
|
||||
process(date)
|
||||
}
|
||||
} catch {
|
||||
|
||||
}
|
||||
|
@ -442,6 +442,13 @@ public final class ChatListNode: ListView {
|
||||
|
||||
private let viewProcessingQueue = Queue()
|
||||
private var chatListView: ChatListNodeView?
|
||||
var entriesCount: Int {
|
||||
if let chatListView = self.chatListView {
|
||||
return chatListView.filteredEntries.count
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
private var interaction: ChatListNodeInteraction?
|
||||
|
||||
private var dequeuedInitialTransitionOnLayout = false
|
||||
|
@ -220,7 +220,7 @@ class ContactsAddItemNode: ListViewItemNode {
|
||||
if let updatedIcon = updatedIcon {
|
||||
strongSelf.iconNode.image = updatedIcon
|
||||
}
|
||||
transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(x: 14.0, y: 5.0, width: 40.0, height: 40.0))
|
||||
transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(x: params.leftInset + 14.0, y: 5.0, width: 40.0, height: 40.0))
|
||||
|
||||
let topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height))
|
||||
|
@ -383,7 +383,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)))
|
||||
|
||||
self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
if !hadValidLayout {
|
||||
while !self.enqueuedTransitions.isEmpty {
|
||||
|
@ -123,6 +123,28 @@ private enum FileIconImage: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
final class CachedChatListSearchResult {
|
||||
let text: String
|
||||
let searchQuery: String
|
||||
let resultRanges: [Range<String.Index>]
|
||||
|
||||
init(text: String, searchQuery: String, resultRanges: [Range<String.Index>]) {
|
||||
self.text = text
|
||||
self.searchQuery = searchQuery
|
||||
self.resultRanges = resultRanges
|
||||
}
|
||||
|
||||
func matches(text: String, searchQuery: String) -> Bool {
|
||||
if self.text != text {
|
||||
return false
|
||||
}
|
||||
if self.searchQuery != searchQuery {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public final class ListMessageFileItemNode: ListMessageNode {
|
||||
private let contextSourceNode: ContextExtractedContentContainingNode
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
@ -139,6 +161,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
private var selectionNode: ItemListSelectableControlNode?
|
||||
|
||||
public let titleNode: TextNode
|
||||
public let textNode: TextNode
|
||||
public let descriptionNode: TextNode
|
||||
private let descriptionProgressNode: ImmediateTextNode
|
||||
public let dateNode: TextNode
|
||||
@ -170,6 +193,8 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
private var contentSizeValue: CGSize?
|
||||
private var currentLeftOffset: CGFloat = 0.0
|
||||
|
||||
private var cachedChatListSearchResult: CachedChatListSearchResult?
|
||||
|
||||
public required init() {
|
||||
self.contextSourceNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
@ -189,6 +214,9 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
|
||||
self.textNode = TextNode()
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
|
||||
self.descriptionNode = TextNode()
|
||||
self.descriptionNode.isUserInteractionEnabled = false
|
||||
@ -231,6 +259,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
self.contextSourceNode.contentNode.addSubnode(self.extractedBackgroundImageNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.offsetContainerNode)
|
||||
self.offsetContainerNode.addSubnode(self.titleNode)
|
||||
self.offsetContainerNode.addSubnode(self.textNode)
|
||||
self.offsetContainerNode.addSubnode(self.descriptionNode)
|
||||
self.offsetContainerNode.addSubnode(self.descriptionProgressNode)
|
||||
self.offsetContainerNode.addSubnode(self.dateNode)
|
||||
@ -307,6 +336,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
|
||||
override public func asyncLayout() -> (_ item: ListMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: Bool, _ mergedBottom: Bool, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) {
|
||||
let titleNodeMakeLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let textNodeMakeLayout = TextNode.asyncLayout(self.textNode)
|
||||
let descriptionNodeMakeLayout = TextNode.asyncLayout(self.descriptionNode)
|
||||
let extensionIconTextMakeLayout = TextNode.asyncLayout(self.extensionIconText)
|
||||
let dateNodeMakeLayout = TextNode.asyncLayout(self.dateNode)
|
||||
@ -315,6 +345,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
let currentMedia = self.currentMedia
|
||||
let currentMessage = self.message
|
||||
let currentIconImage = self.currentIconImage
|
||||
let currentChatListSearchResult = self.cachedChatListSearchResult
|
||||
|
||||
let currentItem = self.appliedItem
|
||||
|
||||
@ -547,6 +578,36 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
}
|
||||
|
||||
var chatListSearchResult: CachedChatListSearchResult?
|
||||
if let searchQuery = item.interaction.searchTextHighightState {
|
||||
if let cached = currentChatListSearchResult, cached.matches(text: item.message.text, searchQuery: searchQuery) {
|
||||
chatListSearchResult = cached
|
||||
} else {
|
||||
let (ranges, text) = findSubstringRanges(in: item.message.text, query: searchQuery)
|
||||
chatListSearchResult = CachedChatListSearchResult(text: text, searchQuery: searchQuery, resultRanges: ranges)
|
||||
}
|
||||
} else {
|
||||
chatListSearchResult = nil
|
||||
}
|
||||
|
||||
var captionText: NSMutableAttributedString?
|
||||
if let chatListSearchResult = chatListSearchResult, !chatListSearchResult.resultRanges.isEmpty, let firstRange = chatListSearchResult.resultRanges.first {
|
||||
let text = NSMutableAttributedString(string: item.message.text, font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
for range in chatListSearchResult.resultRanges {
|
||||
let stringRange = NSRange(range, in: chatListSearchResult.text)
|
||||
if stringRange.location >= 0 && stringRange.location + stringRange.length <= text.length {
|
||||
text.addAttribute(.foregroundColor, value: item.presentationData.theme.theme.chatList.messageHighlightedTextColor, range: stringRange)
|
||||
}
|
||||
}
|
||||
captionText = text
|
||||
|
||||
let firstRangeOrigin = item.message.text.distance(from: item.message.text.startIndex, to: firstRange.lowerBound)
|
||||
if firstRangeOrigin > 20 {
|
||||
captionText = text.attributedSubstring(from: NSMakeRange(firstRangeOrigin - 10, text.length - firstRangeOrigin + 10)).mutableCopy() as? NSMutableAttributedString
|
||||
captionText?.insert(NSAttributedString(string: "\u{2026}", attributes: [NSAttributedString.Key.font: descriptionFont, NSAttributedString.Key.foregroundColor: item.presentationData.theme.theme.list.itemSecondaryTextColor]), at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
let dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: item.message.timestamp, relativeTo: timestamp, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||
let dateAttributedString = NSAttributedString(string: dateText, font: dateFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
@ -555,6 +616,8 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
|
||||
let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(TextNodeLayoutArguments(attributedString: titleText, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .middle, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - rightInset - dateNodeLayout.size.width - 4.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (textNodeLayout, textNodeApply) = textNodeMakeLayout(TextNodeLayoutArguments(attributedString: captionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 30.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(TextNodeLayoutArguments(attributedString: descriptionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 30.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (extensionTextLayout, extensionTextApply) = extensionIconTextMakeLayout(TextNodeLayoutArguments(attributedString: extensionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 38.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
@ -600,7 +663,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
insets.top += header.height
|
||||
}
|
||||
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 8.0 * 2.0 + titleNodeLayout.size.height + 3.0 + descriptionNodeLayout.size.height), insets: insets)
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 8.0 * 2.0 + titleNodeLayout.size.height + 3.0 + descriptionNodeLayout.size.height + (textNodeLayout.size.height > 0.0 ? textNodeLayout.size.height + 3.0 : 0.0)), insets: insets)
|
||||
|
||||
return (nodeLayout, { animation in
|
||||
if let strongSelf = self {
|
||||
@ -683,7 +746,10 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: strongSelf.descriptionNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset + descriptionOffset, y: strongSelf.titleNode.frame.maxY + 1.0), size: descriptionNodeLayout.size))
|
||||
transition.updateFrame(node: strongSelf.textNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset + descriptionOffset, y: strongSelf.titleNode.frame.maxY + 1.0), size: textNodeLayout.size))
|
||||
let _ = textNodeApply()
|
||||
|
||||
transition.updateFrame(node: strongSelf.descriptionNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset + descriptionOffset, y: strongSelf.titleNode.frame.maxY + 1.0 + (textNodeLayout.size.height > 0.0 ? textNodeLayout.size.height + 3.0 : 0.0)), size: descriptionNodeLayout.size))
|
||||
let _ = descriptionNodeApply()
|
||||
|
||||
let _ = dateNodeApply()
|
||||
|
@ -19,6 +19,8 @@ public final class ListMessageItemInteraction {
|
||||
let longTap: (ChatControllerInteractionLongTapAction, Message?) -> Void
|
||||
let getHiddenMedia: () -> [MessageId: [Media]]
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
|
||||
public init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, getHiddenMedia: @escaping () -> [MessageId: [Media]]) {
|
||||
self.openMessage = openMessage
|
||||
self.openMessageContextMenu = openMessageContextMenu
|
||||
|
@ -54,6 +54,8 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
|
||||
private var appliedItem: ListMessageItem?
|
||||
|
||||
private var cachedChatListSearchResult: CachedChatListSearchResult?
|
||||
|
||||
public required init() {
|
||||
self.contextSourceNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
@ -212,6 +214,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
let currentIconImageRepresentation = self.currentIconImageRepresentation
|
||||
|
||||
let currentItem = self.appliedItem
|
||||
let currentChatListSearchResult = self.cachedChatListSearchResult
|
||||
|
||||
let selectionNodeLayout = ItemListSelectableControlNode.asyncLayout(self.selectionNode)
|
||||
|
||||
@ -439,6 +442,36 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
}
|
||||
}
|
||||
|
||||
var chatListSearchResult: CachedChatListSearchResult?
|
||||
if let searchQuery = item.interaction.searchTextHighightState {
|
||||
if let cached = currentChatListSearchResult, cached.matches(text: item.message.text, searchQuery: searchQuery) {
|
||||
chatListSearchResult = cached
|
||||
} else {
|
||||
let (ranges, text) = findSubstringRanges(in: item.message.text, query: searchQuery)
|
||||
chatListSearchResult = CachedChatListSearchResult(text: text, searchQuery: searchQuery, resultRanges: ranges)
|
||||
}
|
||||
} else {
|
||||
chatListSearchResult = nil
|
||||
}
|
||||
|
||||
if let chatListSearchResult = chatListSearchResult, !chatListSearchResult.resultRanges.isEmpty, let firstRange = chatListSearchResult.resultRanges.first {
|
||||
var text = NSMutableAttributedString(string: item.message.text, font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
for range in chatListSearchResult.resultRanges {
|
||||
let stringRange = NSRange(range, in: chatListSearchResult.text)
|
||||
if stringRange.location >= 0 && stringRange.location + stringRange.length <= text.length {
|
||||
text.addAttribute(.foregroundColor, value: item.presentationData.theme.theme.chatList.messageHighlightedTextColor, range: stringRange)
|
||||
}
|
||||
}
|
||||
|
||||
let firstRangeOrigin = item.message.text.distance(from: item.message.text.startIndex, to: firstRange.lowerBound)
|
||||
if firstRangeOrigin > 20 {
|
||||
text = text.attributedSubstring(from: NSMakeRange(firstRangeOrigin - 10, text.length - firstRangeOrigin + 10)).mutableCopy() as! NSMutableAttributedString
|
||||
text.insert(NSAttributedString(string: "\u{2026}", attributes: [NSAttributedString.Key.font: descriptionFont, NSAttributedString.Key.foregroundColor: item.presentationData.theme.theme.list.itemSecondaryTextColor]), at: 0)
|
||||
}
|
||||
|
||||
descriptionText = text
|
||||
}
|
||||
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
let dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: item.message.timestamp, relativeTo: timestamp, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||
let dateAttributedString = NSAttributedString(string: dateText, font: dateFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
|
Loading…
x
Reference in New Issue
Block a user