mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge pull request #6413 from vector-im/feature/bma/room_member_loading
Show a loader if all the Room Member are not yet loaded.
This commit is contained in:
commit
58580f1e6a
12 changed files with 155 additions and 22 deletions
1
changelog.d/6413.feature
Normal file
1
changelog.d/6413.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Show a loader if all the Room Members are not yet loaded.
|
|
@ -53,6 +53,13 @@ class FlowRoom(private val room: Room) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveAreAllMembersLoaded(): Flow<Boolean> {
|
||||||
|
return room.membershipService().areAllMembersLoadedLive().asFlow()
|
||||||
|
.startWith(room.coroutineDispatchers.io) {
|
||||||
|
room.membershipService().areAllMembersLoaded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun liveAnnotationSummary(eventId: String): Flow<Optional<EventAnnotationsSummary>> {
|
fun liveAnnotationSummary(eventId: String): Flow<Optional<EventAnnotationsSummary>> {
|
||||||
return room.relationService().getEventAnnotationsSummaryLive(eventId).asFlow()
|
return room.relationService().getEventAnnotationsSummaryLive(eventId).asFlow()
|
||||||
.startWith(room.coroutineDispatchers.io) {
|
.startWith(room.coroutineDispatchers.io) {
|
||||||
|
|
|
@ -30,6 +30,20 @@ interface MembershipService {
|
||||||
*/
|
*/
|
||||||
suspend fun loadRoomMembersIfNeeded()
|
suspend fun loadRoomMembersIfNeeded()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the room members can be not loaded, for instance after an initial sync.
|
||||||
|
* All the members will be loaded when calling [loadRoomMembersIfNeeded], or when sending an encrypted
|
||||||
|
* event to the room.
|
||||||
|
* The fun let the app know if all the members have been loaded for this room.
|
||||||
|
* @return true if all the members are loaded, or false elsewhere.
|
||||||
|
*/
|
||||||
|
suspend fun areAllMembersLoaded(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Live version for [areAllMembersLoaded].
|
||||||
|
*/
|
||||||
|
fun areAllMembersLoadedLive(): LiveData<Boolean>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the roomMember with userId or null.
|
* Return the roomMember with userId or null.
|
||||||
* @param userId the userId param to look for
|
* @param userId the userId param to look for
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.session.room
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Transformations
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import io.realm.Realm
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
||||||
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class RoomDataSource @Inject constructor(
|
||||||
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
|
) {
|
||||||
|
fun getRoomMembersLoadStatus(roomId: String): RoomMembersLoadStatusType {
|
||||||
|
var result: RoomMembersLoadStatusType?
|
||||||
|
Realm.getInstance(monarchy.realmConfiguration).use {
|
||||||
|
result = RoomEntity.where(it, roomId).findFirst()?.membersLoadStatus
|
||||||
|
}
|
||||||
|
return result ?: RoomMembersLoadStatusType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRoomMembersLoadStatusLive(roomId: String): LiveData<Boolean> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{
|
||||||
|
RoomEntity.where(it, roomId)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
it.membersLoadStatus == RoomMembersLoadStatusType.LOADED
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return Transformations.map(liveData) { results ->
|
||||||
|
results.firstOrNull().orFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,10 +31,12 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.query.QueryStringValueProcessor
|
import org.matrix.android.sdk.internal.query.QueryStringValueProcessor
|
||||||
import org.matrix.android.sdk.internal.query.process
|
import org.matrix.android.sdk.internal.query.process
|
||||||
|
import org.matrix.android.sdk.internal.session.room.RoomDataSource
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.admin.MembershipAdminTask
|
import org.matrix.android.sdk.internal.session.room.membership.admin.MembershipAdminTask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.joining.InviteTask
|
import org.matrix.android.sdk.internal.session.room.membership.joining.InviteTask
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.threepid.InviteThreePidTask
|
import org.matrix.android.sdk.internal.session.room.membership.threepid.InviteThreePidTask
|
||||||
|
@ -47,6 +49,7 @@ internal class DefaultMembershipService @AssistedInject constructor(
|
||||||
private val inviteTask: InviteTask,
|
private val inviteTask: InviteTask,
|
||||||
private val inviteThreePidTask: InviteThreePidTask,
|
private val inviteThreePidTask: InviteThreePidTask,
|
||||||
private val membershipAdminTask: MembershipAdminTask,
|
private val membershipAdminTask: MembershipAdminTask,
|
||||||
|
private val roomDataSource: RoomDataSource,
|
||||||
@UserId
|
@UserId
|
||||||
private val userId: String,
|
private val userId: String,
|
||||||
private val queryStringValueProcessor: QueryStringValueProcessor
|
private val queryStringValueProcessor: QueryStringValueProcessor
|
||||||
|
@ -62,6 +65,15 @@ internal class DefaultMembershipService @AssistedInject constructor(
|
||||||
loadRoomMembersTask.execute(params)
|
loadRoomMembersTask.execute(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun areAllMembersLoaded(): Boolean {
|
||||||
|
val status = roomDataSource.getRoomMembersLoadStatus(roomId)
|
||||||
|
return status == RoomMembersLoadStatusType.LOADED
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areAllMembersLoadedLive(): LiveData<Boolean> {
|
||||||
|
return roomDataSource.getRoomMembersLoadStatusLive(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getRoomMember(userId: String): RoomMemberSummary? {
|
override fun getRoomMember(userId: String): RoomMemberSummary? {
|
||||||
val roomMemberEntity = monarchy.fetchCopied {
|
val roomMemberEntity = monarchy.fetchCopied {
|
||||||
RoomMemberHelper(it, roomId).getLastRoomMember(userId)
|
RoomMemberHelper(it, roomId).getLastRoomMember(userId)
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.matrix.android.sdk.internal.session.room.membership
|
package org.matrix.android.sdk.internal.session.room.membership
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.createObject
|
import io.realm.kotlin.createObject
|
||||||
import kotlinx.coroutines.TimeoutCancellationException
|
import kotlinx.coroutines.TimeoutCancellationException
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
@ -38,6 +37,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
|
import org.matrix.android.sdk.internal.session.room.RoomDataSource
|
||||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
||||||
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
@ -58,6 +58,7 @@ internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Unit>
|
||||||
internal class DefaultLoadRoomMembersTask @Inject constructor(
|
internal class DefaultLoadRoomMembersTask @Inject constructor(
|
||||||
private val roomAPI: RoomAPI,
|
private val roomAPI: RoomAPI,
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
|
private val roomDataSource: RoomDataSource,
|
||||||
private val syncTokenStore: SyncTokenStore,
|
private val syncTokenStore: SyncTokenStore,
|
||||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||||
|
@ -68,7 +69,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
|
||||||
) : LoadRoomMembersTask {
|
) : LoadRoomMembersTask {
|
||||||
|
|
||||||
override suspend fun execute(params: LoadRoomMembersTask.Params) {
|
override suspend fun execute(params: LoadRoomMembersTask.Params) {
|
||||||
when (getRoomMembersLoadStatus(params.roomId)) {
|
when (roomDataSource.getRoomMembersLoadStatus(params.roomId)) {
|
||||||
RoomMembersLoadStatusType.NONE -> doRequest(params)
|
RoomMembersLoadStatusType.NONE -> doRequest(params)
|
||||||
RoomMembersLoadStatusType.LOADING -> waitPreviousRequestToFinish(params)
|
RoomMembersLoadStatusType.LOADING -> waitPreviousRequestToFinish(params)
|
||||||
RoomMembersLoadStatusType.LOADED -> Unit
|
RoomMembersLoadStatusType.LOADED -> Unit
|
||||||
|
@ -142,14 +143,6 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRoomMembersLoadStatus(roomId: String): RoomMembersLoadStatusType {
|
|
||||||
var result: RoomMembersLoadStatusType?
|
|
||||||
Realm.getInstance(monarchy.realmConfiguration).use {
|
|
||||||
result = RoomEntity.where(it, roomId).findFirst()?.membersLoadStatus
|
|
||||||
}
|
|
||||||
return result ?: RoomMembersLoadStatusType.NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun setRoomMembersLoadStatus(roomId: String, status: RoomMembersLoadStatusType) {
|
private suspend fun setRoomMembersLoadStatus(roomId: String, status: RoomMembersLoadStatusType) {
|
||||||
monarchy.awaitTransaction { realm ->
|
monarchy.awaitTransaction { realm ->
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
|
@ -114,6 +115,7 @@ class RoomMemberListFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { viewState ->
|
override fun invalidate() = withState(viewModel) { viewState ->
|
||||||
|
views.roomSettingGeneric.progressBar.isGone = viewState.areAllMembersLoaded
|
||||||
roomMemberListController.setData(viewState)
|
roomMemberListController.setData(viewState)
|
||||||
renderRoomSummary(viewState)
|
renderRoomSummary(viewState)
|
||||||
views.inviteUsersButton.isVisible = viewState.actionsPermissions.canInvite
|
views.inviteUsersButton.isVisible = viewState.actionsPermissions.canInvite
|
||||||
|
|
|
@ -28,6 +28,7 @@ import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
@ -66,6 +67,7 @@ class RoomMemberListViewModel @AssistedInject constructor(
|
||||||
companion object : MavericksViewModelFactory<RoomMemberListViewModel, RoomMemberListViewState> by hiltMavericksViewModelFactory()
|
companion object : MavericksViewModelFactory<RoomMemberListViewModel, RoomMemberListViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
private val room = session.getRoom(initialState.roomId)!!
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
|
private val roomFlow = room.flow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeRoomMemberSummaries()
|
observeRoomMemberSummaries()
|
||||||
|
@ -82,8 +84,8 @@ class RoomMemberListViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
combine(
|
combine(
|
||||||
room.flow().liveRoomMembers(roomMemberQueryParams),
|
roomFlow.liveRoomMembers(roomMemberQueryParams),
|
||||||
room.flow()
|
roomFlow
|
||||||
.liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
.liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
|
||||||
.mapOptional { it.content.toModel<PowerLevelsContent>() }
|
.mapOptional { it.content.toModel<PowerLevelsContent>() }
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -94,8 +96,19 @@ class RoomMemberListViewModel @AssistedInject constructor(
|
||||||
copy(roomMemberSummaries = async)
|
copy(roomMemberSummaries = async)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roomFlow.liveAreAllMembersLoaded()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.onEach {
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
areAllMembersLoaded = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
if (room.roomCryptoService().isEncrypted()) {
|
if (room.roomCryptoService().isEncrypted()) {
|
||||||
room.flow().liveRoomMembers(roomMemberQueryParams)
|
roomFlow.liveRoomMembers(roomMemberQueryParams)
|
||||||
.flatMapLatest { membersSummary ->
|
.flatMapLatest { membersSummary ->
|
||||||
session.cryptoService().getLiveCryptoDeviceInfo(membersSummary.map { it.userId })
|
session.cryptoService().getLiveCryptoDeviceInfo(membersSummary.map { it.userId })
|
||||||
.asFlow()
|
.asFlow()
|
||||||
|
@ -138,7 +151,7 @@ class RoomMemberListViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.flow().liveRoomSummary()
|
roomFlow.liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
copy(roomSummary = async)
|
copy(roomSummary = async)
|
||||||
|
@ -146,7 +159,7 @@ class RoomMemberListViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeThirdPartyInvites() {
|
private fun observeThirdPartyInvites() {
|
||||||
room.flow()
|
roomFlow
|
||||||
.liveStateEvents(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE), QueryStringValue.IsNotNull)
|
.liveStateEvents(setOf(EventType.STATE_ROOM_THIRD_PARTY_INVITE), QueryStringValue.IsNotNull)
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
copy(threePidInvites = async)
|
copy(threePidInvites = async)
|
||||||
|
|
|
@ -32,6 +32,7 @@ data class RoomMemberListViewState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val roomSummary: Async<RoomSummary> = Uninitialized,
|
val roomSummary: Async<RoomSummary> = Uninitialized,
|
||||||
val roomMemberSummaries: Async<RoomMemberSummaries> = Uninitialized,
|
val roomMemberSummaries: Async<RoomMemberSummaries> = Uninitialized,
|
||||||
|
val areAllMembersLoaded: Boolean = false,
|
||||||
val ignoredUserIds: List<String> = emptyList(),
|
val ignoredUserIds: List<String> = emptyList(),
|
||||||
val filter: String = "",
|
val filter: String = "",
|
||||||
val threePidInvites: Async<List<Event>> = Uninitialized,
|
val threePidInvites: Async<List<Event>> = Uninitialized,
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isGone
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Loading
|
import com.airbnb.mvrx.Loading
|
||||||
|
@ -32,8 +33,6 @@ import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.platform.OnBackPressed
|
import im.vector.app.core.platform.OnBackPressed
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
|
||||||
import im.vector.app.core.resources.DrawableProvider
|
|
||||||
import im.vector.app.databinding.FragmentRecyclerviewWithSearchBinding
|
import im.vector.app.databinding.FragmentRecyclerviewWithSearchBinding
|
||||||
import im.vector.app.features.roomprofile.members.RoomMemberListAction
|
import im.vector.app.features.roomprofile.members.RoomMemberListAction
|
||||||
import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
|
import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
|
||||||
|
@ -45,8 +44,6 @@ import reactivecircus.flowbinding.appcompat.queryTextChanges
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class SpacePeopleFragment @Inject constructor(
|
class SpacePeopleFragment @Inject constructor(
|
||||||
private val drawableProvider: DrawableProvider,
|
|
||||||
private val colorProvider: ColorProvider,
|
|
||||||
private val epoxyController: SpacePeopleListController
|
private val epoxyController: SpacePeopleListController
|
||||||
) : VectorBaseFragment<FragmentRecyclerviewWithSearchBinding>(),
|
) : VectorBaseFragment<FragmentRecyclerviewWithSearchBinding>(),
|
||||||
OnBackPressed, SpacePeopleListController.InteractionListener {
|
OnBackPressed, SpacePeopleListController.InteractionListener {
|
||||||
|
@ -64,6 +61,7 @@ class SpacePeopleFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(membersViewModel) { memberListState ->
|
override fun invalidate() = withState(membersViewModel) { memberListState ->
|
||||||
|
views.progressBar.isGone = memberListState.areAllMembersLoaded
|
||||||
val memberCount = (memberListState.roomSummary.invoke()?.otherMemberIds?.size ?: 0) + 1
|
val memberCount = (memberListState.roomSummary.invoke()?.otherMemberIds?.size ?: 0) + 1
|
||||||
|
|
||||||
toolbar?.subtitle = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount)
|
toolbar?.subtitle = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount)
|
||||||
|
|
|
@ -27,8 +27,26 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:minHeight="0dp"
|
android:minHeight="0dp"
|
||||||
app:title="@string/bottom_action_people"
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"/>
|
app:title="@string/bottom_action_people" />
|
||||||
|
|
||||||
|
<!-- Trick to remove surrounding padding (clip from wrapping frame) -->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="3dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
style="@style/Widget.Vector.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="14dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<androidx.appcompat.widget.SearchView
|
<androidx.appcompat.widget.SearchView
|
||||||
android:id="@+id/memberNameFilter"
|
android:id="@+id/memberNameFilter"
|
||||||
|
|
|
@ -113,6 +113,25 @@
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<!-- Trick to remove surrounding padding (clip from wrapping frame) -->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="3dp"
|
||||||
|
android:elevation="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
style="@style/Widget.Vector.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="14dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<include
|
<include
|
||||||
|
|
Loading…
Reference in a new issue