mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-28 06:00:43 +00:00
Load more search results while scrolling
This commit is contained in:
parent
f60ade542a
commit
b4e64ca75a
@ -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
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user