Load more search results while scrolling

This commit is contained in:
Peter 2019-10-15 18:51:50 +04:00
parent f60ade542a
commit b4e64ca75a
2 changed files with 159 additions and 18 deletions

View File

@ -187,7 +187,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
private let unblockingPeer = ValuePromise<Bool>(false, ignoreRepeated: true)
private let searching = ValuePromise<Bool>(false, ignoreRepeated: true)
private let searchResult = Promise<(SearchMessagesResult, SearchMessagesState)?>()
private let searchResult = Promise<(SearchMessagesResult, SearchMessagesState, SearchMessagesLocation)?>()
private let loadingMessage = ValuePromise<Bool>(false, ignoreRepeated: true)
private var preloadHistoryPeerId: PeerId?
@ -3227,9 +3227,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = (strongSelf.searchResult.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] searchResult in
if let strongSelf = self, let searchResult = searchResult?.0 {
let controller = ChatSearchResultsController(context: strongSelf.context, searchQuery: searchData.query, messages: searchResult.messages, navigateToMessageIndex: { index in
if let strongSelf = self, let (searchResult, searchState, searchLocation) = searchResult {
let controller = ChatSearchResultsController(context: strongSelf.context, location: searchLocation, searchQuery: searchData.query, searchResult: searchResult, searchState: searchState, navigateToMessageIndex: { index in
strongSelf.interfaceInteraction?.navigateMessageSearch(.index(index))
}, resultsUpdated: { results, state in
guard let strongSelf = self else {
return
}
let updatedValue: (SearchMessagesResult, SearchMessagesState, SearchMessagesLocation)? = (results, state, searchLocation)
strongSelf.searchResult.set(.single(updatedValue))
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in
if let data = current.search {
let messageIndices = results.messages.map({ $0.index }).sorted()
var currentIndex = messageIndices.last
if let previousResultId = data.resultsState?.currentId {
for index in messageIndices {
if index.id >= previousResultId {
currentIndex = index
break
}
}
}
return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: messageIndices, currentId: currentIndex?.id, state: state, totalCount: results.totalCount, completed: results.completed)))
} else {
return current
}
})
})
strongSelf.chatDisplayNode.dismissInput()
if case let .inline(navigationController) = strongSelf.presentationInterfaceState.mode {
@ -4958,15 +4982,37 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var items: [ActionSheetItem] = []
if self.presentationInterfaceState.isScheduledMessages {
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ScheduledMessages_ClearAllConfirmation, color: .destructive, action: { [weak actionSheet] in
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ScheduledMessages_ClearAllConfirmation, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
beginClear(.scheduledMessages)
guard let strongSelf = self else {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
beginClear(.scheduledMessages)
})
], parseMarkdown: true), in: .window(.root))
}))
} else if canRemoveGlobally {
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
beginClear(.forEveryone)
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
beginClear(.forEveryone)
})
], parseMarkdown: true), in: .window(.root))
}))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
@ -4974,9 +5020,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}))
} else {
items.append(ActionSheetTextItem(title: text))
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak actionSheet] in
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
beginClear(.forLocalPeer)
guard let strongSelf = self else {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
beginClear(.forLocalPeer)
})
], parseMarkdown: true), in: .window(.root))
}))
}
@ -6094,7 +6151,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let search = searchMessages(account: self.context.account, location: searchState.location, query: searchState.query, state: nil, limit: limit)
|> delay(0.2, queue: Queue.mainQueue())
self.searchResult.set(search |> map(Optional.init))
self.searchResult.set(search
|> map { (result, state) -> (SearchMessagesResult, SearchMessagesState, SearchMessagesLocation)? in
return (result, state, searchState.location)
})
searchDisposable.set((search
|> deliverOnMainQueue).start(next: { [weak self] results, updatedState in

View File

@ -105,7 +105,10 @@ private func chatListSearchContainerPreparedTransition(from fromEntries: [ChatLi
class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
private let context: AccountContext
private var presentationData: PresentationData
private let messages: [Message]
private let location: SearchMessagesLocation
private let searchQuery: String
private var searchResult: SearchMessagesResult
private var searchState: SearchMessagesState
private var interaction: ChatListNodeInteraction?
@ -114,14 +117,23 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
private var validLayout: (ContainerViewLayout, CGFloat)?
var resultsUpdated: ((SearchMessagesResult, SearchMessagesState) -> Void)?
var resultSelected: ((Int) -> Void)?
private let presentationDataPromise: Promise<ChatListPresentationData>
private let disposable = MetaDisposable()
init(context: AccountContext, searchQuery: String, messages: [Message]) {
private var isLoadingMore = false
private let loadMoreDisposable = MetaDisposable()
private let previousEntries = Atomic<[ChatListSearchEntry]?>(value: nil)
init(context: AccountContext, location: SearchMessagesLocation, searchQuery: String, searchResult: SearchMessagesResult, searchState: SearchMessagesState) {
self.context = context
self.messages = messages
self.location = location
self.searchQuery = searchQuery
self.searchResult = searchResult
self.searchState = searchState
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations))
@ -138,7 +150,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|> map { presentationData -> [ChatListSearchEntry] in
var entries: [ChatListSearchEntry] = []
for message in messages {
for message in searchResult.messages {
var peer = RenderedPeer(message: message)
if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference {
if let channelPeer = message.peers[migrationReference.peerId] {
@ -156,8 +168,8 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}, togglePeerSelected: { _ in
}, messageSelected: { [weak self] peer, message, _ in
if let strongSelf = self {
if let index = strongSelf.messages.firstIndex(where: { $0.index == message.index }) {
strongSelf.resultSelected?(strongSelf.messages.count - index - 1)
if let index = strongSelf.searchResult.messages.firstIndex(where: { $0.index == message.index }) {
strongSelf.resultSelected?(strongSelf.searchResult.messages.count - index - 1)
}
strongSelf.listNode.clearHighlightAnimated(true)
}
@ -175,17 +187,86 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
interaction.searchTextHighightState = searchQuery
self.interaction = interaction
let previousEntries = Atomic<[ChatListSearchEntry]?>(value: nil)
self.disposable.set((signal
|> deliverOnMainQueue).start(next: { [weak self] entries in
if let strongSelf = self {
let previousEntries = previousEntries.swap(entries)
let previousEntries = strongSelf.previousEntries.swap(entries)
let firstTime = previousEntries == nil
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, context: context, interaction: interaction)
strongSelf.enqueueTransition(transition, firstTime: firstTime)
}
}))
self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in
guard let strongSelf = self else {
return
}
guard case let .known(value) = offset, value < 100.0 else {
return
}
if strongSelf.searchResult.completed {
return
}
if strongSelf.isLoadingMore {
return
}
strongSelf.loadMore()
}
}
deinit {
self.disposable.dispose()
self.loadMoreDisposable.dispose()
}
private func loadMore() {
self.isLoadingMore = true
self.loadMoreDisposable.set((searchMessages(account: self.context.account, location: self.location, query: self.searchQuery, state: self.searchState)
|> deliverOnMainQueue).start(next: { [weak self] (updatedResult, updatedState) in
guard let strongSelf = self else {
return
}
guard let interaction = strongSelf.interaction else {
return
}
strongSelf.isLoadingMore = false
strongSelf.searchResult = updatedResult
strongSelf.searchState = updatedState
strongSelf.resultsUpdated?(updatedResult, updatedState)
let context = strongSelf.context
let signal = strongSelf.presentationDataPromise.get()
|> map { presentationData -> [ChatListSearchEntry] in
var entries: [ChatListSearchEntry] = []
for message in updatedResult.messages {
var peer = RenderedPeer(message: message)
if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference {
if let channelPeer = message.peers[migrationReference.peerId] {
peer = RenderedPeer(peer: channelPeer)
}
}
entries.append(.message(message, peer, nil, presentationData))
}
return entries
}
strongSelf.disposable.set((signal
|> deliverOnMainQueue).start(next: { entries in
if let strongSelf = self {
let previousEntries = strongSelf.previousEntries.swap(entries)
let firstTime = previousEntries == nil
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, context: context, interaction: interaction)
strongSelf.enqueueTransition(transition, firstTime: firstTime)
}
}))
}))
}
func updatePresentationData(_ presentationData: PresentationData) {