Search filters improvements

This commit is contained in:
Ilya Laktyushin 2020-09-10 15:17:21 +03:00
parent a05970c379
commit a353608f4f
12 changed files with 4292 additions and 4355 deletions

View File

@ -5744,16 +5744,22 @@ Any member of this group will be able to see messages in the channel.";
"Call.AccountIsLoggedOnCurrentDevice" = "Sorry, you can't call %@ because that account is logged in to Telegram on the device you're using for the call.";
"ChatList.Search.FilterMedia" = "Media";
"ChatList.Search.FilterPhotos" = "Photos";
"ChatList.Search.FilterVideos" = "Video";
"ChatList.Search.FilterLinks" = "Links";
"ChatList.Search.FilterFiles" = "Files";
"ChatList.Search.FilterMusic" = "Music";
"ChatList.Search.FilterVoice" = "Voice";
"ChatList.Search.NoResults" = "No Results";
"ChatList.Search.NoResultsQueryDescription" = "There were no results for \"%@\".\nTry a new search.";
"ChatList.Search.NoResultsDescription" = "There were no results.\nTry a new search.";
"ChatList.Search.NoResultsFilter" = "Nothing Yet";
"ChatList.Search.NoResultsFitlerMedia" = "Photos and videos from all your chats will be shown here.";
"ChatList.Search.NoResultsFitlerLinks" = "Links from all your chats will be shown here.";
"ChatList.Search.NoResultsFitlerFiles" = "Files from all your chats will be shown here.";
"ChatList.Search.NoResultsFitlerMusic" = "Music from all your chats will be shown here.";
"ChatList.Search.NoResultsFitlerVoice" = "Voice and video messages from all your chats will be shown here.";
"ChatList.Search.Messages_0" = "%@ messages";
"ChatList.Search.Messages_1" = "%@ message";
"ChatList.Search.Messages_2" = "%@ messages";
@ -5761,34 +5767,6 @@ Any member of this group will be able to see messages in the channel.";
"ChatList.Search.Messages_many" = "%@ messages";
"ChatList.Search.Messages_any" = "%@ messages";
"ChatList.Search.Photos_0" = "%@ photos";
"ChatList.Search.Photos_1" = "%@ photo";
"ChatList.Search.Photos_2" = "%@ photos";
"ChatList.Search.Photos_3_10" = "%@ photos";
"ChatList.Search.Photos_many" = "%@ photos";
"ChatList.Search.Photos_any" = "%@ photos";
"ChatList.Search.Links_0" = "%@ links";
"ChatList.Search.Links_1" = "%@ link";
"ChatList.Search.Links_2" = "%@ links";
"ChatList.Search.Links_3_10" = "%@ links";
"ChatList.Search.Links_many" = "%@ links";
"ChatList.Search.Links_any" = "%@ links";
"ChatList.Search.Files_0" = "%@ files";
"ChatList.Search.Files_1" = "%@ file";
"ChatList.Search.Files_2" = "%@ files";
"ChatList.Search.Files_3_10" = "%@ files";
"ChatList.Search.Files_many" = "%@ files";
"ChatList.Search.Files_any" = "%@ files";
"ChatList.Search.Music_0" = "%@ audio files";
"ChatList.Search.Music_1" = "%@ audio file";
"ChatList.Search.Music_2" = "%@ audio files";
"ChatList.Search.Music_3_10" = "%@ audio files";
"ChatList.Search.Music_many" = "%@ audio files";
"ChatList.Search.Music_any" = "%@ audio files";
"Conversation.InputTextAnonymousPlaceholder" = "Send anonymously";
"DialogList.Replies" = "Replies";

View File

@ -22,10 +22,6 @@ public enum ChatListSearchItemHeaderType {
case chatTypes
case faq
case messages(Int32)
case photos(Int32)
case links(Int32)
case files(Int32)
case music(Int32)
fileprivate func title(strings: PresentationStrings) -> String {
switch self {
@ -63,14 +59,6 @@ public enum ChatListSearchItemHeaderType {
return strings.Settings_FrequentlyAskedQuestions
case let .messages(count):
return strings.ChatList_Search_Messages(count)
case let .photos(count):
return strings.ChatList_Search_Photos(count)
case let .links(count):
return strings.ChatList_Search_Links(count)
case let .files(count):
return strings.ChatList_Search_Files(count)
case let .music(count):
return strings.ChatList_Search_Music(count)
}
}
@ -110,14 +98,6 @@ public enum ChatListSearchItemHeaderType {
return .faq
case .messages:
return .messages
case .photos:
return .photos
case .links:
return .links
case .files:
return .files
case .music:
return .music
}
}
}

View File

@ -464,11 +464,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
gesture?.cancel()
}
}
}, arrowAction: {
if let chatPeer = chatPeer {
searchPeer(chatPeer)
}
})
}, arrowAction: nil)
case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType):
var enabled = true
if filter.contains(.onlyWriteable) {
@ -529,37 +525,10 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
}
})
case let .message(message, peer, readState, presentationData, totalCount, selected, displayCustomHeader):
let header: ChatListSearchItemHeader?
if false, enableHeaders && displayCustomHeader {
let type: ChatListSearchItemHeaderType
if let searchOptions = searchOptions {
if searchOptions.messageTags == .photoOrVideo {
type = .photos(totalCount)
} else if searchOptions.messageTags == .webPage {
type = .links(totalCount)
} else if searchOptions.messageTags == .file {
type = .files(totalCount)
} else if searchOptions.messageTags == .music {
type = .music(totalCount)
} else {
type = .messages(totalCount)
}
} else {
type = .messages(totalCount)
}
header = ChatListSearchItemHeader(type: type, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
} else {
header = nil
}
let selection: ChatHistoryMessageSelection
if let selected = selected {
selection = .selectable(selected: selected)
} else {
selection = .none
}
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 tags = searchOptions?.messageTags, tags != .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: displayCustomHeader ? header : nil, isGlobalSearchResult: true)
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: tags == .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: header, enableContextActions: false, hiddenOffset: false, interaction: interaction)
}
@ -1092,8 +1061,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
globalExpandType = .none
}
if let _ = options?.messageTags, finalQuery.isEmpty {
} else if let _ = options?.peerId {
if options?.messageTags != nil || options?.maxDate != nil || options?.peerId != nil {
} else {
let lowercasedQuery = finalQuery.lowercased()
if lowercasedQuery.count > 1 && presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) {
@ -1217,8 +1185,24 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
}
let openUrlImpl: (String) -> Void = { url in
openUserGeneratedUrl(context: context, url: url, concealed: false, present: { c in
present(c, nil)
}, openResolved: { [weak self] resolved in
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in
// self?.openPeer(peerId: peerId, navigation: navigation)
}, sendFile: nil,
sendSticker: nil,
present: { c, a in
present(c, a)
}, dismissInput: {
self?.dismissInput()
}, contentContext: nil)
})
}
openMediaMessageImpl = { [weak self] message, mode in
let _ = context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: {
let _ = context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: true, mode: mode, navigationController: navigationController, dismissInput: {
self?.dismissInput()
}, present: { c, a in
present(c, a)
@ -1226,20 +1210,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
return transitionNodeImpl?(messageId, media)
}, addToTransitionSurface: { view in
addToTransitionSurfaceImpl?(view)
}, openUrl: { [weak self] url in
openUserGeneratedUrl(context: context, url: url, concealed: false, present: { c in
present(c, nil)
}, openResolved: { [weak self] resolved in
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in
// self?.openPeer(peerId: peerId, navigation: navigation)
}, sendFile: nil,
sendSticker: nil,
present: { c, a in
present(c, a)
}, dismissInput: {
self?.dismissInput()
}, contentContext: nil)
})
}, openUrl: { url in
openUrlImpl(url)
}, openPeer: { peer, navigation in
//self?.openPeer(peerId: peer.id, navigation: navigation)
}, callPeer: { _, _ in
@ -1459,40 +1431,35 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let listInteraction = ListMessageItemInteraction(openMessage: { [weak self] message, mode -> Bool in
self?.dismissInput()
let _ = (foundMessages
|> take(1)
|> deliverOnMainQueue).start(next: { m in
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: { [weak self] in
self?.dismissInput()
}, present: { c, a in
present(c, a)
}, transitionNode: { [weak self] messageId, media in
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
if let strongSelf = self {
strongSelf.listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ListMessageNode {
if let result = itemNode.transitionNode(id: messageId, media: media) {
transitionNode = result
}
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: true, mode: mode, navigationController: navigationController, dismissInput: { [weak self] in
self?.dismissInput()
}, present: { c, a in
present(c, a)
}, transitionNode: { [weak self] messageId, media in
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
if let strongSelf = self {
strongSelf.listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ListMessageNode {
if let result = itemNode.transitionNode(id: messageId, media: media) {
transitionNode = result
}
}
}
return transitionNode
}, addToTransitionSurface: { view in
self?.view.addSubview(view)
}, openUrl: { url in
// self?.openUrl(url: url, concealed: false, external: false)
}, openPeer: { peer, navigation in
// self?.openPeer(peerId: peer.id, navigation: navigation)
}, callPeer: { _, _ in
}, enqueueMessage: { _ in
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .custom(messages: foundMessages, at: message.id, loadMore: {
loadMore()
}), gallerySource: .custom(messages: foundMessages, messageId: message.id, loadMore: {
loadMore()
})))
})
return true
}
return transitionNode
}, addToTransitionSurface: { view in
self?.view.addSubview(view)
}, openUrl: { url in
openUrlImpl(url)
}, openPeer: { peer, navigation in
// self?.openPeer(peerId: peer.id, navigation: navigation)
}, callPeer: { _, _ in
}, enqueueMessage: { _ in
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .custom(messages: foundMessages, at: message.id, loadMore: {
loadMore()
}), gallerySource: .custom(messages: foundMessages, messageId: message.id, loadMore: {
loadMore()
})))
}, openMessageContextMenu: { [weak self] message, bool, node, rect, gesture in
self?.messageContextAction(message, node: node, rect: rect, gesture: gesture)
}, toggleMessagesSelection: { messageId, selected in
@ -1500,19 +1467,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
toggleMessageSelectionImpl?(messageId, selected)
}
}, openUrl: { url, _, _, message in
openUserGeneratedUrl(context: context, url: url, concealed: false, present: { c in
present(c, nil)
}, openResolved: { [weak self] resolved in
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in
// self?.openPeer(peerId: peerId, navigation: navigation)
}, sendFile: nil,
sendSticker: nil,
present: { c, a in
present(c, a)
}, dismissInput: {
self?.dismissInput()
}, contentContext: nil)
})
openUrlImpl(url)
}, openInstantPage: { message, data in
if let (webpage, anchor) = instantPageAndAnchor(message: message) {
let pageController = InstantPageController(context: context, webPage: webpage, sourcePeerType: .channel, anchor: anchor)
@ -1631,8 +1586,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
guard let strongSelf = self else {
return
}
let messageTags: MessageTags?
var messageTags: MessageTags? = strongSelf.currentSearchOptions.messageTags
var maxDate: Int32? = strongSelf.currentSearchOptions.maxDate
var peerId: PeerId? = strongSelf.currentSearchOptions.peerId
var peerName: String? = strongSelf.currentSearchOptions.peerName
var clearQuery: Bool = false
switch filter {
case .media:
@ -1646,11 +1603,14 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
case .voice:
messageTags = .voiceOrInstantVideo
case let .date(date, _):
messageTags = strongSelf.currentSearchOptions.messageTags
maxDate = date
clearQuery = true
case let .peer(id, name):
peerId = id
peerName = name
clearQuery = true
}
strongSelf.updateSearchOptions(strongSelf.currentSearchOptions.withUpdatedMessageTags(messageTags).withUpdatedMaxDate(maxDate), clearQuery: clearQuery)
strongSelf.updateSearchOptions(strongSelf.currentSearchOptions.withUpdatedMessageTags(messageTags).withUpdatedMaxDate(maxDate).withUpdatedPeerId(peerId, peerName: peerName), clearQuery: clearQuery)
}
self.mediaStatusDisposable = (combineLatest(context.sharedContext.mediaManager.globalMediaPlayerState, self.searchOptions.get())
@ -1661,6 +1621,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
if let playlistId = state.playlistId as? PeerMessagesMediaPlaylistId, case .custom = playlistId {
if case .music = type, searchOptions?.messageTags == .music {
return .single((account, state, type))
} else if case .voice = type, searchOptions?.messageTags == .voiceOrInstantVideo {
return .single((account, state, type))
} else {
return .single(nil) |> delay(0.1, queue: .mainQueue())
}
@ -1789,14 +1751,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.setQuery?(nil, tokens, self.searchQueryValue ?? "")
}
if options?.peerId == nil {
self.updateSearchState { state in
var state = state
state.expandLocalSearch = false
return state
}
}
self.updatedSearchOptions?(options, self.possibleDate != nil)
}
@ -1920,19 +1874,42 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let displayingResults = transition.displayingResults
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.listNode.isHidden = strongSelf.searchOptionsValue?.messageTags == .photoOrVideo && (strongSelf.searchQueryValue ?? "").isEmpty
let searchOptions = strongSelf.searchOptionsValue
strongSelf.listNode.isHidden = searchOptions?.messageTags == .photoOrVideo && (strongSelf.searchQueryValue ?? "").isEmpty
strongSelf.mediaNode.isHidden = !strongSelf.listNode.isHidden
if !displayingResults {
strongSelf.listNode.isHidden = true
strongSelf.mediaNode.isHidden = true
}
let emptyResultsTitle: String
let emptyResultsText: String
if transition.query.isEmpty {
if !transition.query.isEmpty {
emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResults
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsQueryDescription(transition.query).0
} else {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsDescription
if let searchOptions = searchOptions, searchOptions.messageTags != nil && searchOptions.minDate == nil && searchOptions.maxDate == nil && searchOptions.peerId == nil {
emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResultsFilter
if searchOptions.messageTags == .photoOrVideo {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsFitlerMedia
} else if searchOptions.messageTags == .webPage {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsFitlerLinks
} else if searchOptions.messageTags == .file {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsFitlerFiles
} else if searchOptions.messageTags == .music {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsFitlerMusic
} else if searchOptions.messageTags == .voiceOrInstantVideo {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsFitlerVoice
} else {
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsDescription
}
} else {
emptyResultsTitle = strongSelf.presentationData.strings.ChatList_Search_NoResults
emptyResultsText = strongSelf.presentationData.strings.ChatList_Search_NoResultsDescription
}
}
strongSelf.emptyResultsTitleNode.attributedText = NSAttributedString(string: emptyResultsTitle, font: Font.semibold(17.0), textColor: strongSelf.presentationData.theme.list.freeTextColor)
strongSelf.emptyResultsTextNode.attributedText = NSAttributedString(string: emptyResultsText, font: Font.regular(15.0), textColor: strongSelf.presentationData.theme.list.freeTextColor)
let emptyResults = displayingResults && transition.isEmpty
@ -2085,6 +2062,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let progressDisposable = MetaDisposable()
var progressStarted = false
strongSelf.playlistPreloadDisposable?.dispose()
strongSelf.playlistPreloadDisposable = (signal
|> afterDisposed {
Queue.mainQueue().async {

View File

@ -13,6 +13,7 @@ enum ChatListSearchFilter: Equatable {
case files
case music
case voice
case peer(PeerId, String)
case date(Int32, String)
var id: Int32 {
@ -27,6 +28,8 @@ enum ChatListSearchFilter: Equatable {
return 3
case .voice:
return 4
case .peer:
return 5
case let .date(date, _):
return date
}
@ -113,8 +116,11 @@ 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 .date(_, dateTitle):
title = dateTitle
case let .peer(_, displayTitle):
title = displayTitle
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/User"), color: color)
case let .date(_, displayTitle):
title = displayTitle
icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Calendar"), color: color)
}
@ -252,7 +258,7 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
totalRawTabSize += paneNodeSize.width
}
let minSpacing: CGFloat = 26.0
let minSpacing: CGFloat = 24.0
var spacing = minSpacing
let resolvedSideInset: CGFloat = 16.0 + sideInset
@ -270,10 +276,6 @@ final class ChatListSearchFiltersContainerNode: ASDisplayNode {
}
longTitlesWidth += resolvedSideInset
if longTitlesWidth < size.width && tabSizes.count > 3 {
spacing = (size.width - titlesWidth - resolvedSideInset * 2.0) / CGFloat(tabSizes.count - 1)
}
let verticalOffset: CGFloat = -3.0
for i in 0 ..< tabSizes.count {
let (_, paneNodeSize, paneNode, wasAdded) = tabSizes[i]

View File

@ -12,7 +12,6 @@ import RadialStatusNode
import TelegramStringFormatting
import UniversalMediaPlayer
import ListMessageItem
import ListSectionHeaderNode
import ChatMessageInteractiveMediaBadge
import ShimmerEffect
import GridMessageSelectionNode
@ -645,7 +644,6 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
private let contentType: ContentType
private let scrollNode: ASScrollNode
private var headerNode: ListSectionHeaderNode
private let floatingHeaderNode: FloatingHeaderNode
private var flashHeaderDelayTimer: Foundation.Timer?
private var isDeceleratingAfterTracking = false
@ -686,9 +684,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
self.scrollNode = ASScrollNode()
self.floatingHeaderNode = FloatingHeaderNode()
self.floatingHeaderNode.alpha = 0.0
self.headerNode = ListSectionHeaderNode(theme: context.sharedContext.currentPresentationData.with { $0 }.theme)
super.init()
self._itemInteraction = VisualMediaItemInteraction(
@ -765,14 +761,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
self.initialized = true
if let (size, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams {
if loading {
self.headerNode.title = ""
} else if totalCount > 0 {
self.headerNode.title = presentationData.strings.ChatList_Search_Photos(totalCount).uppercased()
}
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: true, transition: .immediate)
self.headerNode.alpha = self.mediaItems.isEmpty && !loading ? 0.0 : 1.0
if !self.didSetReady {
self.didSetReady = true
self.ready.set(.single(true))
@ -841,10 +830,8 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
self.currentParams = (size, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData)
let headerSize = CGSize(width: size.width, height: 0.0)
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: 0.0, y: headerSize.height), size: CGSize(width: size.width, height: size.height - headerSize.height)))
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)))
let availableWidth = size.width - sideInset * 2.0

View File

@ -458,11 +458,26 @@ public final class ListMessageFileItemNode: ListMessageNode {
}
titleText = NSAttributedString(string: authorName, font: audioTitleFont, textColor: item.presentationData.theme.theme.list.itemPrimaryTextColor)
let dateString = stringForFullDate(timestamp: item.message.timestamp, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat)
let descriptionString: String
var descriptionString: String = ""
if let duration = file.duration {
descriptionString = "\(stringForDuration(Int32(duration)))\(dateString)"
if item.isGlobalSearchResult {
descriptionString = stringForDuration(Int32(duration))
} else {
descriptionString = "\(stringForDuration(Int32(duration)))\(dateString)"
}
} else {
descriptionString = dateString
if !item.isGlobalSearchResult {
descriptionString = dateString
}
}
if item.isGlobalSearchResult {
let authorString = fullAuthorString(for: item)
if descriptionString.isEmpty {
descriptionString = authorString
} else {
descriptionString = "\(descriptionString)\(authorString)"
}
}
descriptionText = NSAttributedString(string: descriptionString, font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)

View File

@ -312,6 +312,21 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
}
}
for media in item.message.media {
if let image = media as? TelegramMediaImage {
if let representation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 80, height: 80)) {
iconImageReferenceAndRepresentation = (.message(message: MessageReference(item.message), media: image), representation)
}
break
}
if let file = media as? TelegramMediaFile {
if let representation = smallestImageRepresentation(file.previewRepresentations) {
iconImageReferenceAndRepresentation = (.message(message: MessageReference(item.message), media: file), representation)
}
break
}
}
var entities: [MessageTextEntity]?
entities = messageEntities
@ -680,9 +695,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
}
}
} else {
if !item.interaction.openMessage(item.message, .default) {
item.interaction.openUrl(currentPrimaryUrl, false, false, nil)
}
item.interaction.openUrl(currentPrimaryUrl, false, false, nil)
}
}
}

View File

@ -133,7 +133,9 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
control = .seek(time)
}
if (file.isVoice || file.isInstantVideo) && params.message.tags.contains(.voiceOrInstantVideo) {
if params.standalone {
if let playlistLocation = params.playlistLocation {
location = playlistLocation
} else if params.standalone {
location = .recentActions(params.message)
} else {
location = .messages(peerId: params.message.id.peerId, tagMask: .voiceOrInstantVideo, at: params.message.id)

View File

@ -345,10 +345,6 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
self.network = network
self.messagesLocation = location
if case .custom = location {
self.order = .reversed
}
switch self.messagesLocation {
case let .messages(_, _, messageId), let .singleMessage(messageId), let .custom(_, messageId, _):
self.loadItem(anchor: .messageId(messageId), navigation: .later)
@ -679,12 +675,13 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
inputIndex = .single(index)
case .random:
var playbackStack = self.playbackStack
inputIndex = self.postbox.transaction { transaction -> MessageIndex in
inputIndex = messages
|> map { messages, _, _ -> MessageIndex in
if case let .random(previous) = navigation, previous {
let _ = playbackStack.pop()
while true {
if let id = playbackStack.pop() {
if let message = transaction.getMessage(id) {
if let message = messages.first(where: { $0.id == id }) {
return message.index
}
} else {
@ -692,9 +689,8 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
}
}
}
return index
// return transaction.findRandomMessage(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: tagMask, ignoreIds: (playbackStack.ids, playbackStack.set)) ?? index
}
return messages.randomElement()?.index ?? index
}
}
let historySignal = inputIndex
|> mapToSignal { inputIndex -> Signal<(Message, [Message])?, NoError> in