Use sender data return from search result

This commit is contained in:
Benoit Marty 2020-10-01 17:03:53 +02:00
parent 4649b2ac1d
commit 3705fa14bd
11 changed files with 69 additions and 37 deletions

View file

@ -83,6 +83,7 @@ interface Room :
* @param beforeLimit how many events before the result are returned.
* @param afterLimit how many events after the result are returned.
* @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned.
* @param callback Callback to get the search result
*/
fun search(searchTerm: String,
nextBatch: String?,

View file

@ -18,6 +18,7 @@
package org.matrix.android.sdk.api.session.search
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.util.MatrixItem
/**
* Domain class to represent the response of a search request in a room.
@ -35,5 +36,10 @@ data class SearchResult(
/**
* List of results in the requested order.
*/
val results: List<Event>? = null
val results: List<EventAndSender>? = null
)
data class EventAndSender(
val event: Event,
val sender: MatrixItem.UserItem?
)

View file

@ -36,6 +36,7 @@ interface SearchService {
* @param beforeLimit how many events before the result are returned.
* @param afterLimit how many events after the result are returned.
* @param includeProfile requests that the server returns the historic profile information for the users that sent the events that were returned.
* @param callback Callback to get the search result
*/
fun search(searchTerm: String,
roomId: String,

View file

@ -18,7 +18,9 @@
package org.matrix.android.sdk.internal.session.search
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.session.search.EventAndSender
import org.matrix.android.sdk.api.session.search.SearchResult
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody
import org.matrix.android.sdk.internal.session.search.request.SearchRequestCategories
@ -76,7 +78,21 @@ internal class DefaultSearchTask @Inject constructor(
return SearchResult(
nextBatch = searchCategories.roomEvents?.nextBatch,
highlights = searchCategories.roomEvents?.highlights,
results = searchCategories.roomEvents?.results?.map { it.event }?.reversed()
results = searchCategories.roomEvents?.results?.map { searchResponseItem ->
EventAndSender(
searchResponseItem.event,
searchResponseItem.event.senderId?.let { senderId ->
searchResponseItem.context?.profileInfo?.get(senderId)
?.let {
MatrixItem.UserItem(
senderId,
it["displayname"] as? String,
it["avatar_url"] as? String
)
}
}
)
}?.reversed()
)
}
}

View file

@ -38,5 +38,5 @@ internal data class SearchResponseEventContext(
val end: String? = null,
// The historic profile information of the users that sent the events returned. The string key is the user ID for which the profile belongs to.
@Json(name = "profile_info")
val profileInfo: JsonDict? = null
val profileInfo: Map<String, JsonDict>? = null
)

View file

@ -70,7 +70,8 @@ class SearchActivity : VectorBaseActivity() {
fun newIntent(context: Context, args: SearchArgs): Intent {
return Intent(context, SearchActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
// If we do that we will have the same room two times on the stack. Let's allow infinite stack for the moment.
// flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
putExtra(MvRx.KEY_ARG, args)
}
}

View file

@ -90,7 +90,7 @@ class SearchFragment @Inject constructor(
is Loading -> {
stateView.state = StateView.State.Loading
}
is Fail -> {
is Fail -> {
stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncSearchRequest.error))
}
is Success -> {
@ -100,8 +100,7 @@ class SearchFragment @Inject constructor(
}
}
} else {
val lastBatchSize = state.lastBatch?.size ?: 0
pendingScrollToPosition = if (lastBatchSize > 0) lastBatchSize - 1 else 0
pendingScrollToPosition = (state.lastBatchSize - 1).coerceAtLeast(0)
stateView.state = StateView.State.Content
controller.setData(state)

View file

@ -25,6 +25,8 @@ import im.vector.app.core.ui.list.genericItemHeader
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.search.EventAndSender
import org.matrix.android.sdk.api.util.toMatrixItem
import java.util.Calendar
import javax.inject.Inject
@ -62,15 +64,15 @@ class SearchResultController @Inject constructor(
}
}
buildSearchResultItems(data.searchResult.orEmpty())
buildSearchResultItems(data.searchResult)
}
private fun buildSearchResultItems(events: List<Event>) {
private fun buildSearchResultItems(events: List<EventAndSender>) {
var lastDate: Calendar? = null
events.forEach { event ->
events.forEach { eventAndSender ->
val eventDate = Calendar.getInstance().apply {
timeInMillis = event.originServerTs ?: System.currentTimeMillis()
timeInMillis = eventAndSender.event.originServerTs ?: System.currentTimeMillis()
}
if (lastDate?.get(Calendar.DAY_OF_YEAR) != eventDate.get(Calendar.DAY_OF_YEAR)) {
genericItemHeader {
@ -81,13 +83,13 @@ class SearchResultController @Inject constructor(
lastDate = eventDate
searchResultItem {
id(event.eventId)
id(eventAndSender.event.eventId)
avatarRenderer(avatarRenderer)
dateFormatter(dateFormatter)
event(event)
// I think we should use the data returned by the server?
sender(event.senderId?.let { session.getUser(it) })
listener { listener?.onItemClicked(event) }
event(eventAndSender.event)
sender(eventAndSender.sender
?: eventAndSender.event.senderId?.let { session.getUser(it) }?.toMatrixItem())
listener { listener?.onItemClicked(eventAndSender.event) }
}
}
}

View file

@ -27,10 +27,10 @@ import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.api.util.MatrixItem
@EpoxyModelClass(layout = R.layout.item_search_result)
abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
@ -38,15 +38,15 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var dateFormatter: VectorDateFormatter? = null
@EpoxyAttribute lateinit var event: Event
@EpoxyAttribute var sender: User? = null
@EpoxyAttribute var sender: MatrixItem? = null
@EpoxyAttribute var listener: ClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.view.onClick(listener)
sender?.toMatrixItem()?.let { avatarRenderer.render(it, holder.avatarImageView) }
holder.memberNameView.text = sender?.getBestName()
sender?.let { avatarRenderer.render(it, holder.avatarImageView) }
holder.memberNameView.setTextOrHide(sender?.getBestName())
holder.timeView.text = dateFormatter?.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE)
// TODO Improve that (use formattedBody, etc.)
holder.contentView.text = event.content?.get("body") as? String

View file

@ -69,9 +69,14 @@ class SearchViewModel @AssistedInject constructor(
}
private fun handleSearchWith(action: SearchAction.SearchWith) {
if (action.searchTerm.length > 1) {
if (action.searchTerm.isNotEmpty()) {
setState {
copy(searchTerm = action.searchTerm)
copy(
searchResult = emptyList(),
hasMoreResult = false,
lastBatchSize = 0,
searchTerm = action.searchTerm
)
}
startSearching(false)
}
@ -100,9 +105,7 @@ class SearchViewModel @AssistedInject constructor(
}
}
if (state.asyncSearchRequest is Loading) {
currentTask?.cancel()
}
currentTask?.cancel()
viewModelScope.launch {
try {
@ -118,24 +121,22 @@ class SearchViewModel @AssistedInject constructor(
callback = it
)
}
onSearchResultSuccess(result, isNextBatch)
onSearchResultSuccess(result)
} catch (failure: Throwable) {
if (failure is Failure.Cancelled) return@launch
_viewEvents.post(SearchViewEvents.Failure(failure))
setState {
copy(
asyncSearchRequest = Fail(failure),
searchResult = null
asyncSearchRequest = Fail(failure)
)
}
}
}
}
private fun onSearchResultSuccess(searchResult: SearchResult, isNextBatch: Boolean) = withState { state ->
// Accumulate results if it is the next batch
val accumulatedResult = searchResult.results.orEmpty().plus(state.searchResult?.takeIf { isNextBatch }.orEmpty())
private fun onSearchResultSuccess(searchResult: SearchResult) = withState { state ->
val accumulatedResult = searchResult.results.orEmpty().plus(state.searchResult)
// Note: We do not care about the highlights for the moment, but it will be the same algorithm
@ -145,9 +146,14 @@ class SearchViewModel @AssistedInject constructor(
copy(
searchResult = accumulatedResult,
hasMoreResult = !nextBatch.isNullOrEmpty(),
lastBatch = searchResult.results,
lastBatchSize = searchResult.results.orEmpty().size,
asyncSearchRequest = Success(Unit)
)
}
}
override fun onCleared() {
currentTask?.cancel()
super.onCleared()
}
}

View file

@ -19,14 +19,14 @@ package im.vector.app.features.home.room.detail.search
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.search.EventAndSender
data class SearchViewState(
// Accumulated search result
val searchResult: List<Event>? = null,
val searchResult: List<EventAndSender> = emptyList(),
val hasMoreResult: Boolean = false,
// Last batch result will help RecyclerView to position itself
val lastBatch: List<Event>? = null,
// Last batch size, will help RecyclerView to position itself
val lastBatchSize: Int = 0,
val searchTerm: String? = null,
val roomId: String = "",
// Current pagination request