Search filters fixes

This commit is contained in:
Ilya Laktyushin 2020-09-28 16:39:24 +04:00
parent 9b87791908
commit a8f4663852
6 changed files with 100 additions and 71 deletions

View File

@ -1151,7 +1151,12 @@ final class ChatListControllerNode: ASDisplayNode {
return nil return nil
} }
let contentNode = ChatListSearchContainerNode(context: self.context, filter: [], groupId: self.groupId, openPeer: { [weak self] peer, dismissSearch in var filter: ChatListNodePeersFilter = []
if false, case .group = self.groupId {
filter.insert(.excludeRecent)
}
let contentNode = ChatListSearchContainerNode(context: self.context, filter: filter, groupId: self.groupId, openPeer: { [weak self] peer, dismissSearch in
self?.requestOpenPeerFromSearch?(peer, dismissSearch) self?.requestOpenPeerFromSearch?(peer, dismissSearch)
}, openDisabledPeer: { _ in }, openDisabledPeer: { _ in
}, openRecentPeerOptions: { [weak self] peer in }, openRecentPeerOptions: { [weak self] peer in

View File

@ -39,7 +39,7 @@ private enum ChatListTokenId: Int32 {
final class ChatListSearchInteraction { final class ChatListSearchInteraction {
let openPeer: (Peer, Bool) -> Void let openPeer: (Peer, Bool) -> Void
let openDisabledPeer: (Peer) -> Void let openDisabledPeer: (Peer) -> Void
let openMessage: (Peer, MessageId) -> Void let openMessage: (Peer, MessageId, Bool) -> Void
let openUrl: (String) -> Void let openUrl: (String) -> Void
let clearRecentSearch: () -> Void let clearRecentSearch: () -> Void
let addContact: (String) -> Void let addContact: (String) -> Void
@ -52,7 +52,7 @@ final class ChatListSearchInteraction {
let updateSuggestedPeers: ([Peer], ChatListSearchPaneKey) -> Void let updateSuggestedPeers: ([Peer], ChatListSearchPaneKey) -> Void
let getSelectedMessageIds: () -> Set<MessageId>? let getSelectedMessageIds: () -> Set<MessageId>?
init(openPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, updateSuggestedPeers: @escaping ([Peer], ChatListSearchPaneKey) -> Void, getSelectedMessageIds: @escaping () -> Set<MessageId>?) { init(openPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, updateSuggestedPeers: @escaping ([Peer], ChatListSearchPaneKey) -> Void, getSelectedMessageIds: @escaping () -> Set<MessageId>?) {
self.openPeer = openPeer self.openPeer = openPeer
self.openDisabledPeer = openDisabledPeer self.openDisabledPeer = openDisabledPeer
self.openMessage = openMessage self.openMessage = openMessage
@ -102,7 +102,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let suggestedDates = Promise<[(Date, String?)]>([]) private let suggestedDates = Promise<[(Date?, Date, String?)]>([])
private let suggestedPeers = Promise<[Peer]>([]) private let suggestedPeers = Promise<[Peer]>([])
private var suggestedFilters: [ChatListSearchFilter]? private var suggestedFilters: [ChatListSearchFilter]?
private let suggestedFiltersDisposable = MetaDisposable() private let suggestedFiltersDisposable = MetaDisposable()
@ -149,8 +149,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
} }
}, openDisabledPeer: { peer in }, openDisabledPeer: { peer in
openDisabledPeer(peer) openDisabledPeer(peer)
}, openMessage: { peer, messageId in }, openMessage: { peer, messageId, deactivateOnAction in
originalOpenMessage(peer, messageId, true) originalOpenMessage(peer, messageId, deactivateOnAction)
if peer.id.namespace != Namespaces.Peer.SecretChat { if peer.id.namespace != Namespaces.Peer.SecretChat {
addAppLogEvent(postbox: context.account.postbox, type: "search_global_open_message", peerId: peer.id, data: .dictionary(["msg_id": .number(Double(messageId.id))])) addAppLogEvent(postbox: context.account.postbox, type: "search_global_open_message", peerId: peer.id, data: .dictionary(["msg_id": .number(Double(messageId.id))]))
} }
@ -269,7 +269,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
} }
var key: ChatListSearchPaneKey? var key: ChatListSearchPaneKey?
var maxDate = strongSelf.currentSearchOptions.maxDate var date = strongSelf.currentSearchOptions.date
var peer = strongSelf.currentSearchOptions.peer var peer = strongSelf.currentSearchOptions.peer
switch filter { switch filter {
@ -285,8 +285,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
key = .music key = .music
case .voice: case .voice:
key = .voice key = .voice
case let .date(date, title): case let .date(minDate, maxDate, title):
maxDate = (date, title) date = (minDate, maxDate, title)
case let .peer(id, isGroup, _, compactDisplayTitle): case let .peer(id, isGroup, _, compactDisplayTitle):
peer = (id, isGroup, compactDisplayTitle) peer = (id, isGroup, compactDisplayTitle)
} }
@ -294,16 +294,28 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
if let key = key { if let key = key {
strongSelf.paneContainerNode.requestSelectPane(key) strongSelf.paneContainerNode.requestSelectPane(key)
} else { } else {
strongSelf.updateSearchOptions(strongSelf.currentSearchOptions.withUpdatedMaxDate(maxDate).withUpdatedPeer(peer), clearQuery: true) strongSelf.updateSearchOptions(strongSelf.currentSearchOptions.withUpdatedDate(date).withUpdatedPeer(peer), clearQuery: true)
} }
} }
self.suggestedFiltersDisposable.set((combineLatest(self.suggestedPeers.get(), self.suggestedDates.get(), self.selectedFilterKeyPromise.get()) let suggestedPeers = self.searchQuery.get()
|> mapToSignal { peers, dates, selectedFilter -> Signal<([Peer], [(Date, String?)], ChatListSearchFilterEntryId?), NoError> in |> mapToSignal { query -> Signal<[Peer], NoError> in
if (peers.isEmpty && dates.isEmpty) || peers.isEmpty { if let query = query {
return context.account.postbox.searchPeers(query: query.lowercased())
|> map { local -> [Peer] in
return Array(local.compactMap { $0.peer }.prefix(10))
}
} else {
return .single([])
}
}
self.suggestedFiltersDisposable.set((combineLatest(suggestedPeers, self.suggestedDates.get(), self.selectedFilterKeyPromise.get(), self.searchQuery.get())
|> mapToSignal { peers, dates, selectedFilter, searchQuery -> Signal<([Peer], [(Date?, Date, String?)], ChatListSearchFilterEntryId?), NoError> in
if searchQuery?.isEmpty ?? true {
return .single((peers, dates, selectedFilter)) return .single((peers, dates, selectedFilter))
} else { } else {
return (.complete() |> delay(0.2, queue: Queue.mainQueue())) return (.complete() |> delay(0.25, queue: Queue.mainQueue()))
|> then(.single((peers, dates, selectedFilter))) |> then(.single((peers, dates, selectedFilter)))
} }
} |> map { peers, dates, selectedFilter -> [ChatListSearchFilter] in } |> map { peers, dates, selectedFilter -> [ChatListSearchFilter] in
@ -313,9 +325,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
formatter.timeStyle = .none formatter.timeStyle = .none
formatter.dateStyle = .medium formatter.dateStyle = .medium
for (date, string) in dates { for (minDate, maxDate, string) in dates {
let title = string ?? formatter.string(from: date) let title = string ?? formatter.string(from: maxDate)
suggestedFilters.append(.date(Int32(date.timeIntervalSince1970), title)) suggestedFilters.append(.date(minDate.flatMap { Int32($0.timeIntervalSince1970) }, Int32(maxDate.timeIntervalSince1970), title))
} }
} }
if !peers.isEmpty && selectedFilter != .filter(ChatListSearchFilter.chats.id) { if !peers.isEmpty && selectedFilter != .filter(ChatListSearchFilter.chats.id) {
@ -339,7 +351,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
} }
var filteredFilters: [ChatListSearchFilter] = [] var filteredFilters: [ChatListSearchFilter] = []
for filter in filters { for filter in filters {
if case .date = filter, strongSelf.searchOptionsValue?.maxDate == nil { if case .date = filter, strongSelf.searchOptionsValue?.date == nil {
filteredFilters.append(filter) filteredFilters.append(filter)
} }
if case .peer = filter, strongSelf.searchOptionsValue?.peer == nil { if case .peer = filter, strongSelf.searchOptionsValue?.peer == nil {
@ -390,7 +402,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
} }
private var currentSearchOptions: ChatListSearchOptions { private var currentSearchOptions: ChatListSearchOptions {
return self.searchOptionsValue ?? ChatListSearchOptions(peer: nil, minDate: nil, maxDate: nil) return self.searchOptionsValue ?? ChatListSearchOptions(peer: nil, date: nil)
} }
public override func searchTokensUpdated(tokens: [SearchBarToken]) { public override func searchTokensUpdated(tokens: [SearchBarToken]) {
@ -399,8 +411,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
for token in tokens { for token in tokens {
tokensIdSet.insert(token.id) tokensIdSet.insert(token.id)
} }
if !tokensIdSet.contains(ChatListTokenId.date.rawValue) && updatedOptions?.maxDate != nil { if !tokensIdSet.contains(ChatListTokenId.date.rawValue) && updatedOptions?.date != nil {
updatedOptions = updatedOptions?.withUpdatedMaxDate(nil) updatedOptions = updatedOptions?.withUpdatedDate(nil)
} }
if !tokensIdSet.contains(ChatListTokenId.peer.rawValue) && updatedOptions?.peer != nil { if !tokensIdSet.contains(ChatListTokenId.peer.rawValue) && updatedOptions?.peer != nil {
updatedOptions = updatedOptions?.withUpdatedPeer(nil) updatedOptions = updatedOptions?.withUpdatedPeer(nil)
@ -429,7 +441,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
tokens.append(SearchBarToken(id: ChatListTokenId.peer.rawValue, icon:image, title: peerName)) tokens.append(SearchBarToken(id: ChatListTokenId.peer.rawValue, icon:image, title: peerName))
} }
if let (_, dateTitle) = options?.maxDate { if let (_, _, dateTitle) = options?.date {
tokens.append(SearchBarToken(id: ChatListTokenId.date.rawValue, icon: UIImage(bundleImageName: "Chat List/Search/Calendar"), title: dateTitle)) tokens.append(SearchBarToken(id: ChatListTokenId.date.rawValue, icon: UIImage(bundleImageName: "Chat List/Search/Calendar"), title: dateTitle))
self.suggestedDates.set(.single([])) self.suggestedDates.set(.single([]))

View File

@ -15,7 +15,7 @@ enum ChatListSearchFilter: Equatable {
case music case music
case voice case voice
case peer(PeerId, Bool, String, String) case peer(PeerId, Bool, String, String)
case date(Int32, String) case date(Int32?, Int32, String)
var id: Int32 { var id: Int32 {
switch self { switch self {
@ -33,7 +33,7 @@ enum ChatListSearchFilter: Equatable {
return 5 return 5
case let .peer(peerId, _, _, _): case let .peer(peerId, _, _, _):
return peerId.id return peerId.id
case let .date(date, _): case let .date(_, date, _):
return date return date
} }
} }
@ -139,7 +139,7 @@ private final class ItemNode: ASDisplayNode {
image = UIImage(bundleImageName: "Chat List/Search/User") image = UIImage(bundleImageName: "Chat List/Search/User")
} }
icon = generateTintedImage(image: image, color: color) icon = generateTintedImage(image: image, color: color)
case let .date(_, displayTitle): case let .date(_, _, displayTitle):
title = displayTitle title = displayTitle
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Calendar"), color: color) icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Calendar"), color: color)
} }

View File

@ -615,23 +615,18 @@ public enum ChatListSearchContextActionSource {
public struct ChatListSearchOptions { public struct ChatListSearchOptions {
let peer: (PeerId, Bool, String)? let peer: (PeerId, Bool, String)?
let minDate: (Int32, String)? let date: (Int32?, Int32, String)?
let maxDate: (Int32, String)?
var isEmpty: Bool { var isEmpty: Bool {
return self.peer == nil && self.minDate == nil && self.maxDate == nil return self.peer == nil && self.date == nil
} }
func withUpdatedPeer(_ peerIdIsGroupAndName: (PeerId, Bool, String)?) -> ChatListSearchOptions { func withUpdatedPeer(_ peerIdIsGroupAndName: (PeerId, Bool, String)?) -> ChatListSearchOptions {
return ChatListSearchOptions(peer: peerIdIsGroupAndName, minDate: self.minDate, maxDate: self.maxDate) return ChatListSearchOptions(peer: peerIdIsGroupAndName, date: self.date)
} }
func withUpdatedMinDate(_ minDateAndTitle: (Int32, String)?) -> ChatListSearchOptions { func withUpdatedDate(_ minDateMaxDateAndTitle: (Int32?, Int32, String)?) -> ChatListSearchOptions {
return ChatListSearchOptions(peer: self.peer, minDate: minDateAndTitle, maxDate: self.maxDate) return ChatListSearchOptions(peer: self.peer, date: minDateMaxDateAndTitle)
}
func withUpdatedMaxDate(_ maxDateAndTitle: (Int32, String)?) -> ChatListSearchOptions {
return ChatListSearchOptions(peer: self.peer, minDate: self.minDate, maxDate: maxDateAndTitle)
} }
} }
@ -880,12 +875,12 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let location: SearchMessagesLocation let location: SearchMessagesLocation
if let options = options { if let options = options {
if let (peerId, _, _) = options.peer { if let (peerId, _, _) = options.peer {
location = .peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: nil, minDate: options.minDate?.0, maxDate: options.maxDate?.0) location = .peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: nil, minDate: options.date?.0, maxDate: options.date?.1)
} else { } else {
if let groupId = groupId { if let groupId = groupId {
location = .group(groupId: groupId, tags: tagMask, minDate: options.minDate?.0, maxDate: options.maxDate?.0) location = .group(groupId: groupId, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)
} else { } else {
location = .general(tags: tagMask, minDate: options.minDate?.0, maxDate: options.maxDate?.0) location = .general(tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)
} }
} }
} else { } else {
@ -1208,8 +1203,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}, additionalCategorySelected: { _ in }, additionalCategorySelected: { _ in
}, messageSelected: { [weak self] peer, message, _ in }, messageSelected: { [weak self] peer, message, _ in
interaction.dismissInput() interaction.dismissInput()
if let peer = message.peers[message.id.peerId] { if let strongSelf = self, let peer = message.peers[message.id.peerId] {
interaction.openMessage(peer, message.id) interaction.openMessage(peer, message.id, strongSelf.key == .chats)
} }
self?.listNode.clearHighlightAnimated(true) self?.listNode.clearHighlightAnimated(true)
}, groupSelected: { _ in }, groupSelected: { _ in
@ -1336,7 +1331,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
} }
} }
if strongSelf.tagMask != nil || strongSelf.searchOptionsValue?.maxDate != nil || strongSelf.searchOptionsValue?.peer != nil { if strongSelf.tagMask != nil || strongSelf.searchOptionsValue?.date != nil || strongSelf.searchOptionsValue?.peer != nil {
entriesAndFlags?.0 = filteredEntries entriesAndFlags?.0 = filteredEntries
} }
} }
@ -2013,7 +2008,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResults emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResults
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsQueryDescription(query).0 emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsQueryDescription(query).0
} else { } else {
if let searchOptions = searchOptions, searchOptions.minDate == nil && searchOptions.maxDate == nil && searchOptions.peer == nil { if let searchOptions = searchOptions, searchOptions.date == nil && searchOptions.peer == nil {
emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResultsFilter emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResultsFilter
if strongSelf.tagMask == .photoOrVideo { if strongSelf.tagMask == .photoOrVideo {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsFitlerMedia emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsFitlerMedia
@ -2047,7 +2042,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
strongSelf.emptyResultsTextNode.isHidden = !emptyResults strongSelf.emptyResultsTextNode.isHidden = !emptyResults
strongSelf.emptyResultsAnimationNode.visibility = emptyResults strongSelf.emptyResultsAnimationNode.visibility = emptyResults
let displayPlaceholder = transition.isLoading && (strongSelf.key != .chats || strongSelf.searchOptionsValue?.peer != nil || strongSelf.searchOptionsValue?.minDate != nil || strongSelf.searchOptionsValue?.maxDate != nil) let displayPlaceholder = transition.isLoading && (strongSelf.key != .chats || strongSelf.searchOptionsValue?.peer != nil || strongSelf.searchOptionsValue?.date != nil)
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateAlpha(node: strongSelf.shimmerNode, alpha: displayPlaceholder ? 1.0 : 0.0) 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.recentListNode.isHidden = displayingResults || strongSelf.peersFilter.contains(.excludeRecent)
@ -2274,7 +2269,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message, selection: .none, displayHeader: false, customHeader: nil, hintIsLink: true, isGlobalSearchResult: true) return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message, selection: .none, displayHeader: false, customHeader: nil, hintIsLink: true, isGlobalSearchResult: true)
case .files: case .files:
var media: [Media] = [] var media: [Media] = []
media.append(TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: [.Audio(isVoice: false, duration: 0, title: nil, performer: nil, waveform: MemoryBuffer(data: Data()))])) media.append(TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: 0, attributes: [.FileName(fileName: "Text.txt")]))
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Text", attributes: [], media: media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: timestamp1, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Text", attributes: [], media: media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message, selection: .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true) return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message, selection: .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true)
@ -2356,7 +2351,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX + 120.0 + 10.0, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 60.0) fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX + 120.0 + 10.0, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 60.0)
let dateFrame = itemNode.dateNode.frame.offsetBy(dx: 0.0, dy: currentY) let dateFrame = itemNode.dateNode.frame.offsetBy(dx: 0.0, dy: currentY)
fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: dateFrame.minY), width: 30.0) fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: floor(dateFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 30.0)
context.setBlendMode(.normal) context.setBlendMode(.normal)
context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor) context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor)
@ -2381,7 +2376,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
fillLabelPlaceholderRect(origin: CGPoint(x: descriptionFrame.minX, y: floor(descriptionFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: isVoice ? 60.0 : 240.0) fillLabelPlaceholderRect(origin: CGPoint(x: descriptionFrame.minX, y: floor(descriptionFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: isVoice ? 60.0 : 240.0)
let dateFrame = itemNode.dateNode.frame.offsetBy(dx: 0.0, dy: currentY) let dateFrame = itemNode.dateNode.frame.offsetBy(dx: 0.0, dy: currentY)
fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: dateFrame.minY), width: 30.0) fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: floor(dateFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 30.0)
context.setBlendMode(.normal) context.setBlendMode(.normal)
context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor) context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor)
@ -2401,7 +2396,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
fillLabelPlaceholderRect(origin: CGPoint(x: authorFrame.minX, y: floor(authorFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0) fillLabelPlaceholderRect(origin: CGPoint(x: authorFrame.minX, y: floor(authorFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0)
let dateFrame = itemNode.dateNode.frame.offsetBy(dx: 0.0, dy: currentY) let dateFrame = itemNode.dateNode.frame.offsetBy(dx: 0.0, dy: currentY)
fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: dateFrame.minY), width: 30.0) fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: floor(dateFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 30.0)
context.setBlendMode(.normal) context.setBlendMode(.normal)
context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor) context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor)

View File

@ -4,7 +4,7 @@ import TelegramStringFormatting
private let telegramReleaseDate = Date(timeIntervalSince1970: 1376438400.0) private let telegramReleaseDate = Date(timeIntervalSince1970: 1376438400.0)
func suggestDates(for string: String, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) -> [(Date, String?)] { func suggestDates(for string: String, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) -> [(minDate: Date?, maxDate: Date, string: String?)] {
let string = string.folding(options: .diacriticInsensitive, locale: .current).trimmingCharacters(in: .whitespacesAndNewlines).lowercased() let string = string.folding(options: .diacriticInsensitive, locale: .current).trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if string.count < 3 { if string.count < 3 {
return [] return []
@ -39,9 +39,14 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
let yesterday = strings.Weekday_Yesterday let yesterday = strings.Weekday_Yesterday
let dateSeparator = dateTimeFormat.dateSeparator let dateSeparator = dateTimeFormat.dateSeparator
var result: [(Date, String?)] = [] var result: [(Date?, Date, String?)] = []
let calendar = Calendar.current let calendar = Calendar.current
func getLowerDate(for date: Date) -> Date {
let components = calendar.dateComponents(in: .current, from: date)
let upperComponents = DateComponents(year: components.year, month: components.month, day: components.day, hour: 0, minute: 0, second: 0)
return calendar.date(from: upperComponents)!
}
func getUpperDate(for date: Date) -> Date { func getUpperDate(for date: Date) -> Date {
let components = calendar.dateComponents(in: .current, from: date) let components = calendar.dateComponents(in: .current, from: date)
let upperComponents = DateComponents(year: components.year, month: components.month, day: components.day, hour: 23, minute: 59, second: 59) let upperComponents = DateComponents(year: components.year, month: components.month, day: components.day, hour: 23, minute: 59, second: 59)
@ -54,15 +59,23 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
return [] return []
} }
let midnight = calendar.startOfDay(for: now) let midnightDate = calendar.startOfDay(for: now)
if today.lowercased().hasPrefix(string) { if today.lowercased().hasPrefix(string) {
let todayDate = getUpperDate(for: midnight) let todayDate = getUpperDate(for: midnightDate)
result.append((todayDate, today)) result.append((midnightDate, todayDate, today))
} }
if yesterday.lowercased().hasPrefix(string) { if yesterday.lowercased().hasPrefix(string) {
let yesterdayMidnight = calendar.date(byAdding: .day, value: -1, to: midnight)! let yesterdayMidnight = calendar.date(byAdding: .day, value: -1, to: midnightDate)!
let yesterdayDate = getUpperDate(for: yesterdayMidnight) let yesterdayDate = getUpperDate(for: yesterdayMidnight)
result.append((yesterdayDate, yesterday)) result.append((yesterdayMidnight, yesterdayDate, yesterday))
}
func getLowerMonthDate(month: Int, year: Int) -> Date {
let monthComponents = DateComponents(year: year, month: month)
let date = calendar.date(from: monthComponents)!
let range = calendar.range(of: .day, in: .month, for: date)!
let upperComponents = DateComponents(year: year, month: month, day: 1, hour: 0, minute: 0, second: 0)
return calendar.date(from: upperComponents)!
} }
func getUpperMonthDate(month: Int, year: Int) -> Date { func getUpperMonthDate(month: Int, year: Int) -> Date {
@ -77,9 +90,10 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
let decimalRange = string.rangeOfCharacter(from: .decimalDigits) let decimalRange = string.rangeOfCharacter(from: .decimalDigits)
if decimalRange != nil { if decimalRange != nil {
if string.count == 4, let value = Int(string), value <= year { if string.count == 4, let value = Int(string), value <= year {
let date = getUpperMonthDate(month: 12, year: value) let minDate = getLowerMonthDate(month: 1, year: value)
if date > telegramReleaseDate { let maxDate = getUpperMonthDate(month: 12, year: value)
result.append((date, "\(value)")) if maxDate > telegramReleaseDate {
result.append((minDate, maxDate, "\(value)"))
} }
} else { } else {
do { do {
@ -95,11 +109,11 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
if stringComponents.count < 3 { if stringComponents.count < 3 {
for i in 0..<5 { for i in 0..<5 {
if let date = calendar.date(byAdding: .year, value: -i, to: resultDate) { if let date = calendar.date(byAdding: .year, value: -i, to: resultDate) {
result.append((date, nil)) result.append((nil, date, nil))
} }
} }
} else if resultDate < now { } else if resultDate < now {
result.append((resultDate, nil)) result.append((nil, resultDate, nil))
} }
} }
let dd = try NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue) let dd = try NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
@ -121,13 +135,14 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
var nextDateComponent = calendar.dateComponents([.hour, .minute, .second], from: now) var nextDateComponent = calendar.dateComponents([.hour, .minute, .second], from: now)
nextDateComponent.weekday = day + calendar.firstWeekday nextDateComponent.weekday = day + calendar.firstWeekday
if let date = calendar.nextDate(after: now, matching: nextDateComponent, matchingPolicy: .nextTime, direction: .backward) { if let date = calendar.nextDate(after: now, matching: nextDateComponent, matchingPolicy: .nextTime, direction: .backward) {
let upperDate = getUpperDate(for: date) let lowerAnchorDate = getLowerDate(for: date)
let upperAnchorDate = getUpperDate(for: date)
for i in 0..<5 { for i in 0..<5 {
if let date = calendar.date(byAdding: .hour, value: -24 * 7 * i, to: upperDate) { if let lowerDate = calendar.date(byAdding: .hour, value: -24 * 7 * i, to: lowerAnchorDate), let upperDate = calendar.date(byAdding: .hour, value: -24 * 7 * i, to: upperAnchorDate) {
if calendar.isDate(date, equalTo: now, toGranularity: .weekOfYear) { if calendar.isDate(upperDate, equalTo: now, toGranularity: .weekOfYear) {
result.append((date, value.0)) result.append((lowerDate, upperDate, value.0))
} else { } else {
result.append((date, nil)) result.append((lowerDate, upperDate, nil))
} }
} }
} }
@ -143,15 +158,17 @@ func suggestDates(for string: String, strings: PresentationStrings, dateTimeForm
let shortMonthName = value.1.lowercased() let shortMonthName = value.1.lowercased()
if cleanString == shortMonthName || (cleanString.count >= shortMonthName.count && monthName.hasPrefix(cleanString)) { if cleanString == shortMonthName || (cleanString.count >= shortMonthName.count && monthName.hasPrefix(cleanString)) {
if cleanDigits.count == 4, let year = Int(cleanDigits) { if cleanDigits.count == 4, let year = Int(cleanDigits) {
let date = getUpperMonthDate(month: month, year: year) let lowerDate = getLowerMonthDate(month: month, year: year)
if date <= now && date > telegramReleaseDate { let upperDate = getUpperMonthDate(month: month, year: year)
result.append((date, stringForMonth(strings: strings, month: Int32(month - 1), ofYear: Int32(year - 1900)))) if upperDate <= now && upperDate > telegramReleaseDate {
result.append((lowerDate, upperDate, stringForMonth(strings: strings, month: Int32(month - 1), ofYear: Int32(year - 1900))))
} }
} else if cleanDigits.isEmpty { } else if cleanDigits.isEmpty {
for i in (year - 7 ... year).reversed() { for i in (year - 7 ... year).reversed() {
let date = getUpperMonthDate(month: month, year: i) let lowerDate = getUpperMonthDate(month: month, year: i)
if date <= now && date > telegramReleaseDate { let upperDate = getUpperMonthDate(month: month, year: i)
result.append((date, stringForMonth(strings: strings, month: Int32(month - 1), ofYear: Int32(i - 1900)))) if upperDate <= now && upperDate > telegramReleaseDate {
result.append((lowerDate, upperDate, stringForMonth(strings: strings, month: Int32(month - 1), ofYear: Int32(i - 1900))))
} }
} }
} }

View File

@ -286,7 +286,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode {
transition.updateFrame(node: self.emptyResultsTextNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + padding + (layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0 - emptyTextSize.width) / 2.0, y: emptyTitleY + emptyTitleSize.height + emptyTextSpacing), size: emptyTextSize)) transition.updateFrame(node: self.emptyResultsTextNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + padding + (layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0 - emptyTextSize.width) / 2.0, y: emptyTitleY + emptyTitleSize.height + emptyTextSpacing), size: emptyTextSize))
self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size) 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 firstValidLayout { if firstValidLayout {