mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 05:31:21 +03:00
Enhance filtering to support threads
This commit is contained in:
parent
81a1dfd66d
commit
5e23947419
5 changed files with 138 additions and 2 deletions
|
@ -19,6 +19,10 @@ package org.matrix.android.sdk.internal.session.search
|
|||
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.database.RealmSessionProvider
|
||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.search.request.SearchRequestBody
|
||||
|
@ -28,6 +32,7 @@ import org.matrix.android.sdk.internal.session.search.request.SearchRequestFilte
|
|||
import org.matrix.android.sdk.internal.session.search.request.SearchRequestOrder
|
||||
import org.matrix.android.sdk.internal.session.search.request.SearchRequestRoomEvents
|
||||
import org.matrix.android.sdk.internal.session.search.response.SearchResponse
|
||||
import org.matrix.android.sdk.internal.session.search.response.SearchResponseItem
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -47,7 +52,8 @@ internal interface SearchTask : Task<SearchTask.Params, SearchResult> {
|
|||
|
||||
internal class DefaultSearchTask @Inject constructor(
|
||||
private val searchAPI: SearchAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
private val realmSessionProvider: RealmSessionProvider
|
||||
) : SearchTask {
|
||||
|
||||
override suspend fun execute(params: SearchTask.Params): SearchResult {
|
||||
|
@ -74,12 +80,22 @@ internal class DefaultSearchTask @Inject constructor(
|
|||
}
|
||||
|
||||
private fun SearchResponse.toDomain(): SearchResult {
|
||||
val localTimelineEvents = findRootThreadEventsFromDB(searchCategories.roomEvents?.results)
|
||||
return SearchResult(
|
||||
nextBatch = searchCategories.roomEvents?.nextBatch,
|
||||
highlights = searchCategories.roomEvents?.highlights,
|
||||
results = searchCategories.roomEvents?.results?.map { searchResponseItem ->
|
||||
|
||||
val localThreadEventDetails = localTimelineEvents
|
||||
?.firstOrNull { it.eventId == searchResponseItem.event.eventId }
|
||||
?.root
|
||||
?.asDomain()
|
||||
?.threadDetails
|
||||
|
||||
EventAndSender(
|
||||
searchResponseItem.event,
|
||||
searchResponseItem.event.apply {
|
||||
threadDetails = localThreadEventDetails
|
||||
},
|
||||
searchResponseItem.event.senderId?.let { senderId ->
|
||||
searchResponseItem.context?.profileInfo?.get(senderId)
|
||||
?.let {
|
||||
|
@ -94,4 +110,19 @@ internal class DefaultSearchTask @Inject constructor(
|
|||
}?.reversed()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find local events if exists in order to enhance the result with thread summary
|
||||
*/
|
||||
private fun findRootThreadEventsFromDB(searchResponseItemList: List<SearchResponseItem>?): List<TimelineEventEntity>? {
|
||||
return realmSessionProvider.withRealm { realm ->
|
||||
searchResponseItemList?.mapNotNull {
|
||||
it.event.roomId ?: return@mapNotNull null
|
||||
it.event.eventId ?: return@mapNotNull null
|
||||
TimelineEventEntity.where(realm, it.event.roomId, it.event.eventId).findFirst()
|
||||
}?.filter {
|
||||
it.root?.isRootThread == true || it.root?.isThread() == true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@ class SearchResultController @Inject constructor(
|
|||
.spannable(spannable.toEpoxyCharSequence())
|
||||
.sender(eventAndSender.sender
|
||||
?: eventAndSender.event.senderId?.let { session.getRoomMember(it, data.roomId) }?.toMatrixItem())
|
||||
.threadDetails(event.threadDetails)
|
||||
.listener { listener?.onItemClicked(eventAndSender.event) }
|
||||
.let { result.add(it) }
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ package im.vector.app.features.home.room.detail.search
|
|||
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
|
@ -29,6 +32,7 @@ import im.vector.app.core.epoxy.onClick
|
|||
import im.vector.app.core.extensions.setTextOrHide
|
||||
import im.vector.app.features.displayname.getBestName
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import org.matrix.android.sdk.api.session.threads.ThreadDetails
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_search_result)
|
||||
|
@ -38,6 +42,8 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
|
|||
@EpoxyAttribute var formattedDate: String? = null
|
||||
@EpoxyAttribute lateinit var spannable: EpoxyCharSequence
|
||||
@EpoxyAttribute var sender: MatrixItem? = null
|
||||
@EpoxyAttribute var threadDetails: ThreadDetails? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
|
@ -48,6 +54,36 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
|
|||
holder.memberNameView.setTextOrHide(sender?.getBestName())
|
||||
holder.timeView.text = formattedDate
|
||||
holder.contentView.text = spannable.charSequence
|
||||
|
||||
if (BuildConfig.THREADING_ENABLED) {
|
||||
threadDetails?.let {
|
||||
if (it.isRootThread) {
|
||||
showThreadSummary(holder)
|
||||
holder.threadSummaryCounterTextView.text = it.numberOfThreads.toString()
|
||||
holder.threadSummaryInfoTextView.text = it.threadSummaryLatestTextMessage.orEmpty()
|
||||
|
||||
val userId = it.threadSummarySenderInfo?.userId ?: return@let
|
||||
val displayName = it.threadSummarySenderInfo?.displayName
|
||||
val avatarUrl = it.threadSummarySenderInfo?.avatarUrl
|
||||
avatarRenderer.render(MatrixItem.UserItem(userId, displayName, avatarUrl), holder.threadSummaryAvatarImageView)
|
||||
} else {
|
||||
showFromThread(holder)
|
||||
}
|
||||
} ?: run {
|
||||
holder.threadSummaryConstraintLayout.isVisible = false
|
||||
holder.fromThreadConstraintLayout.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showThreadSummary(holder: Holder, show: Boolean = true) {
|
||||
holder.threadSummaryConstraintLayout.isVisible = show
|
||||
holder.fromThreadConstraintLayout.isVisible = !show
|
||||
}
|
||||
|
||||
private fun showFromThread(holder: Holder, show: Boolean = true) {
|
||||
holder.threadSummaryConstraintLayout.isVisible = !show
|
||||
holder.fromThreadConstraintLayout.isVisible = show
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
|
@ -55,5 +91,10 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
|
|||
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||
val timeView by bind<TextView>(R.id.messageTimeView)
|
||||
val contentView by bind<TextView>(R.id.messageContentView)
|
||||
val threadSummaryConstraintLayout by bind<ConstraintLayout>(R.id.searchThreadSummaryConstraintLayout)
|
||||
val threadSummaryCounterTextView by bind<TextView>(R.id.messageThreadSummaryCounterTextView)
|
||||
val threadSummaryAvatarImageView by bind<ImageView>(R.id.messageThreadSummaryAvatarImageView)
|
||||
val threadSummaryInfoTextView by bind<TextView>(R.id.messageThreadSummaryInfoTextView)
|
||||
val fromThreadConstraintLayout by bind<ConstraintLayout>(R.id.searchFromThreadConstraintLayout)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,4 +62,66 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/messageMemberNameView"
|
||||
tools:text="@sample/messages.json/data/message" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/searchThreadSummaryConstraintLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
android:layout_below="@id/informationBottom"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
android:background="@drawable/rounded_rect_shape_8"
|
||||
android:contentDescription="@string/room_threads_filter"
|
||||
android:maxWidth="496dp"
|
||||
android:minWidth="144dp"
|
||||
android:paddingStart="13dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="13dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/messageContentView"
|
||||
app:layout_constraintStart_toStartOf="@id/messageContentView"
|
||||
app:layout_constraintTop_toBottomOf="@id/messageContentView"
|
||||
tools:visibility="gone">
|
||||
|
||||
<include layout="@layout/view_thread_room_summary" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/searchFromThreadConstraintLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/messageContentView"
|
||||
app:layout_constraintTop_toBottomOf="@id/messageContentView"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/searchFromThreadImageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:contentDescription="@string/room_threads_filter"
|
||||
android:src="@drawable/ic_thread_summary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/searchFromThreadTextView"
|
||||
style="@style/Widget.Vector.TextView.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:minEms="1"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/searchFromThreadImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:text="@string/search_thread_from_a_thread" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -1049,6 +1049,7 @@
|
|||
<string name="thread_list_empty_subtitle">Threads help keep your conversations on-topic and easy to track.</string>
|
||||
<!-- Parameter %s will be replaced by the value of string reply_in_thread -->
|
||||
<string name="thread_list_empty_notice">Tip: Long tap a message and use “%s”.</string>
|
||||
<string name="search_thread_from_a_thread">From a Thread</string>
|
||||
|
||||
<!-- Room events -->
|
||||
<string name="room_event_action_report_prompt_reason">Reason for reporting this content</string>
|
||||
|
|
Loading…
Reference in a new issue