(DRAFT) Room counter flow

This commit is contained in:
Maxime Naturel 2022-02-07 09:55:24 +01:00
parent eb38c9d835
commit c7dae341c0
9 changed files with 68 additions and 0 deletions
matrix-sdk-android
build.gradle
src/main/java/org/matrix/android/sdk
api/session/room
internal/session/room
vector/src/main/java/im/vector/app/features/home/room/list

View file

@ -73,6 +73,9 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
freeCompilerArgs += [
"-Xopt-in=kotlin.RequiresOptIn"
]
} }
sourceSets { sourceSets {

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.paging.PagedList import androidx.paging.PagedList
import kotlinx.coroutines.flow.Flow
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
@ -216,6 +217,11 @@ interface RoomService {
pagedListConfig: PagedList.Config = defaultPagedListConfig, pagedListConfig: PagedList.Config = defaultPagedListConfig,
sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): UpdatableLivePageResult sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): UpdatableLivePageResult
/**
* Retrieve a flow on the the number of rooms.
*/
fun getRoomCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int>
/** /**
* TODO Doc * TODO Doc
*/ */

View file

@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.Transformations
import androidx.paging.PagedList import androidx.paging.PagedList
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import kotlinx.coroutines.flow.Flow
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.RoomService import org.matrix.android.sdk.api.session.room.RoomService
@ -109,6 +110,10 @@ internal class DefaultRoomService @Inject constructor(
return roomSummaryDataSource.getUpdatablePagedRoomSummariesLive(queryParams, pagedListConfig, sortOrder) return roomSummaryDataSource.getUpdatablePagedRoomSummariesLive(queryParams, pagedListConfig, sortOrder)
} }
override fun getRoomCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int> {
return roomSummaryDataSource.getCountFlow(queryParams)
}
override fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount { override fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount {
return roomSummaryDataSource.getNotificationCountForRooms(queryParams) return roomSummaryDataSource.getNotificationCountForRooms(queryParams)
} }

View file

@ -25,7 +25,12 @@ import androidx.paging.PagedList
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.kotlin.toFlow
import io.realm.kotlin.where import io.realm.kotlin.where
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.ActiveSpaceFilter
import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.query.isNormalized import org.matrix.android.sdk.api.query.isNormalized
@ -42,6 +47,7 @@ import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotification
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
@ -55,6 +61,7 @@ import javax.inject.Inject
internal class RoomSummaryDataSource @Inject constructor( internal class RoomSummaryDataSource @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider,
private val roomSummaryMapper: RoomSummaryMapper, private val roomSummaryMapper: RoomSummaryMapper,
private val queryStringValueProcessor: QueryStringValueProcessor private val queryStringValueProcessor: QueryStringValueProcessor
) { ) {
@ -230,6 +237,28 @@ internal class RoomSummaryDataSource @Inject constructor(
} }
} }
// @OptIn(ExperimentalCoroutinesApi::class)
// fun getCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int> = callbackFlow {
// val realmResult = realmSessionProvider.withRealm { realm ->
// roomSummariesQuery(realm, queryParams).findAllAsync()
// }
// val changeListener = RealmChangeListener<RealmResults<RoomSummaryEntity>> {
// trySendBlocking(it.size)
// .onFailure { throwable -> Timber.e(throwable) }
// }
// realmResult.addChangeListener(changeListener)
// awaitClose { realmResult.removeChangeListener(changeListener) }
// }
fun getCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int> =
// TODO handle properly threads and dispatchers otherwise use livedata of monarchy
realmSessionProvider
.withRealm { realm -> roomSummariesQuery(realm, queryParams).findAllAsync() }
.toFlow()
.map { it.size }
.flowOn(Dispatchers.IO)
// TODO should we improve how we update notification count with flow ??
fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount { fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount {
var notificationCount: RoomAggregateNotificationCount? = null var notificationCount: RoomAggregateNotificationCount? = null
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->

View file

@ -29,6 +29,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
// TODO check where it is used in the project
@EpoxyModelClass(layout = R.layout.item_room_category) @EpoxyModelClass(layout = R.layout.item_room_category)
abstract class RoomCategoryItem : VectorEpoxyModel<RoomCategoryItem.Holder>() { abstract class RoomCategoryItem : VectorEpoxyModel<RoomCategoryItem.Holder>() {

View file

@ -287,6 +287,12 @@ class RoomListFragment @Inject constructor(
)) ))
checkEmptyState() checkEmptyState()
} }
// TODO use flow if possible ?
section.itemCount.observe(viewLifecycleOwner) { count ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
itemCount = count
))
}
section.notificationCount.observe(viewLifecycleOwner) { counts -> section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy( sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
notificationCount = counts.totalCount, notificationCount = counts.totalCount,
@ -326,6 +332,12 @@ class RoomListFragment @Inject constructor(
isLoading = false)) isLoading = false))
checkEmptyState() checkEmptyState()
} }
// TODO use flow instead ?
section.itemCount.observe(viewLifecycleOwner) { count ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
itemCount = count
))
}
section.notificationCount.observe(viewLifecycleOwner) { counts -> section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy( sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
notificationCount = counts.totalCount, notificationCount = counts.totalCount,

View file

@ -242,6 +242,7 @@ class RoomListSectionBuilderGroup(
@StringRes nameRes: Int, @StringRes nameRes: Int,
notifyOfLocalEcho: Boolean = false, notifyOfLocalEcho: Boolean = false,
query: (RoomSummaryQueryParams.Builder) -> Unit) { query: (RoomSummaryQueryParams.Builder) -> Unit) {
// TODO check when this class is used: difference with RoomListSectionBuilderSpace ?
withQueryParams( withQueryParams(
{ query.invoke(it) }, { query.invoke(it) },
{ roomQueryParams -> { roomQueryParams ->
@ -251,6 +252,7 @@ class RoomListSectionBuilderGroup(
activeSpaceUpdaters.add(it) activeSpaceUpdaters.add(it)
}.livePagedList }.livePagedList
.let { livePagedList -> .let { livePagedList ->
// TODO should we improve this ?
// use it also as a source to update count // use it also as a source to update count
livePagedList.asFlow() livePagedList.asFlow()
.onEach { .onEach {

View file

@ -374,6 +374,7 @@ class RoomListSectionBuilderSpace(
// use it also as a source to update count // use it also as a source to update count
livePagedList.asFlow() livePagedList.asFlow()
.onEach { .onEach {
// TODO should we improve this ?
Timber.v("Thread space list: ${Thread.currentThread()}") Timber.v("Thread space list: ${Thread.currentThread()}")
sections.find { it.sectionName == name } sections.find { it.sectionName == name }
?.notificationCount ?.notificationCount
@ -398,6 +399,12 @@ class RoomListSectionBuilderSpace(
) )
) )
} }
// TODO extract into a dedicated private method
session.getRoomCountFlow(roomQueryParams)
.onEach { count -> sections.find { section -> section.sectionName == name }?.itemCount?.postValue(count) }
.flowOn(Dispatchers.Default)
.launchIn(viewModelScope)
} }
) )

View file

@ -26,9 +26,12 @@ data class RoomsSection(
val sectionName: String, val sectionName: String,
// can be a paged list or a regular list // can be a paged list or a regular list
val livePages: LiveData<PagedList<RoomSummary>>? = null, val livePages: LiveData<PagedList<RoomSummary>>? = null,
// TODO liveList is not used : can we delete ?
val liveList: LiveData<List<RoomSummary>>? = null, val liveList: LiveData<List<RoomSummary>>? = null,
val liveSuggested: LiveData<SuggestedRoomInfo>? = null, val liveSuggested: LiveData<SuggestedRoomInfo>? = null,
val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true), val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true),
// TODO expose a Flow<Int> instead ?
val itemCount: MutableLiveData<Int> = MutableLiveData(0),
val notificationCount: MutableLiveData<RoomAggregateNotificationCount> = MutableLiveData(RoomAggregateNotificationCount(0, 0)), val notificationCount: MutableLiveData<RoomAggregateNotificationCount> = MutableLiveData(RoomAggregateNotificationCount(0, 0)),
val notifyOfLocalEcho: Boolean = false val notifyOfLocalEcho: Boolean = false
) )