Room navigation : more reliable roomInfoFlow method
This commit is contained in:
@@ -44,13 +44,11 @@ import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -61,7 +59,7 @@ import kotlin.jvm.optionals.getOrNull
|
||||
class RoomFlowNode @AssistedInject constructor(
|
||||
@Assisted val buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val roomListService: RoomListService,
|
||||
private val client: MatrixClient,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
private val joinRoomEntryPoint: JoinRoomEntryPoint,
|
||||
) : BaseFlowNode<RoomFlowNode.NavTarget>(
|
||||
@@ -92,18 +90,16 @@ class RoomFlowNode @AssistedInject constructor(
|
||||
|
||||
override fun onBuilt() {
|
||||
super.onBuilt()
|
||||
roomListService.getUserMembershipForRoom(
|
||||
client.getRoomInfoFlow(
|
||||
inputs.roomId
|
||||
).flowOn(Dispatchers.Default)
|
||||
.onEach { membership ->
|
||||
Timber.d("RoomMembership = $membership")
|
||||
if (membership.getOrNull() == CurrentUserMembership.JOINED) {
|
||||
backstack.newRoot(NavTarget.JoinedRoom)
|
||||
} else {
|
||||
backstack.newRoot(NavTarget.JoinRoom)
|
||||
}
|
||||
).onEach { roomInfo ->
|
||||
Timber.d("Room membership: ${roomInfo.map { it.currentUserMembership }}")
|
||||
if (roomInfo.getOrNull()?.currentUserMembership == CurrentUserMembership.JOINED) {
|
||||
backstack.newRoot(NavTarget.JoinedRoom)
|
||||
} else {
|
||||
backstack.newRoot(NavTarget.JoinRoom)
|
||||
}
|
||||
.flowOn(Dispatchers.Main)
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
roomMembershipObserver.updates
|
||||
|
||||
@@ -30,14 +30,13 @@ import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
class JoinRoomPresenter @AssistedInject constructor(
|
||||
@Assisted private val roomId: RoomId,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val roomListService: RoomListService,
|
||||
private val acceptDeclineInvitePresenter: AcceptDeclineInvitePresenter,
|
||||
) : Presenter<JoinRoomState> {
|
||||
|
||||
@@ -47,23 +46,24 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||
|
||||
@Composable
|
||||
override fun present(): JoinRoomState {
|
||||
val userMembership by roomListService.getUserMembershipForRoom(roomId).collectAsState(initial = Optional.empty())
|
||||
val joinAuthorisationStatus = joinAuthorisationStatus(userMembership)
|
||||
val mxRoomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty())
|
||||
val joinAuthorisationStatus = joinAuthorisationStatus(mxRoomInfo)
|
||||
val acceptDeclineInviteState = acceptDeclineInvitePresenter.present()
|
||||
val roomInfo by produceState<AsyncData<RoomInfo>>(initialValue = AsyncData.Uninitialized, key1 = userMembership) {
|
||||
val roomInfo by produceState<AsyncData<RoomInfo>>(initialValue = AsyncData.Uninitialized, key1 = mxRoomInfo) {
|
||||
value = when {
|
||||
userMembership.isPresent -> {
|
||||
val roomInfo = matrixClient.getRoom(roomId)?.use {
|
||||
mxRoomInfo.isPresent -> {
|
||||
val roomInfo = mxRoomInfo.get().let {
|
||||
RoomInfo(
|
||||
roomId = it.roomId,
|
||||
roomName = it.displayName,
|
||||
roomAlias = it.alias,
|
||||
memberCount = it.activeMemberCount,
|
||||
roomId = roomId,
|
||||
roomName = it.name,
|
||||
roomAlias = it.canonicalAlias,
|
||||
memberCount = it.activeMembersCount,
|
||||
isDirect = it.isDirect,
|
||||
topic = it.topic,
|
||||
roomAvatarUrl = it.avatarUrl
|
||||
)
|
||||
}
|
||||
roomInfo?.let { AsyncData.Success(it) } ?: AsyncData.Failure(Exception("Failed to load room info"))
|
||||
AsyncData.Success(roomInfo)
|
||||
}
|
||||
else -> AsyncData.Uninitialized
|
||||
}
|
||||
@@ -103,9 +103,10 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun joinAuthorisationStatus(userMembership: Optional<CurrentUserMembership>): JoinAuthorisationStatus {
|
||||
private fun joinAuthorisationStatus(roomInfo: Optional<MatrixRoomInfo>): JoinAuthorisationStatus {
|
||||
val userMembership = roomInfo.getOrNull()?.currentUserMembership
|
||||
return when {
|
||||
userMembership.getOrNull() == CurrentUserMembership.INVITED -> return JoinAuthorisationStatus.IsInvited
|
||||
userMembership == CurrentUserMembership.INVITED -> return JoinAuthorisationStatus.IsInvited
|
||||
else -> JoinAuthorisationStatus.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,15 +29,16 @@ data class JoinRoomState(
|
||||
val joinAuthorisationStatus: JoinAuthorisationStatus,
|
||||
val acceptDeclineInviteState: AcceptDeclineInviteState,
|
||||
val eventSink: (JoinRoomEvents) -> Unit
|
||||
){
|
||||
) {
|
||||
val showMemberCount = roomInfo.dataOrNull()?.memberCount != null
|
||||
}
|
||||
|
||||
data class RoomInfo(
|
||||
val roomId: RoomId,
|
||||
val roomName: String,
|
||||
val roomName: String?,
|
||||
val roomAlias: String?,
|
||||
val memberCount: Long?,
|
||||
val topic: String?,
|
||||
val isDirect: Boolean,
|
||||
val roomAvatarUrl: String?,
|
||||
) {
|
||||
|
||||
@@ -48,6 +48,7 @@ fun aJoinRoomState(
|
||||
roomAlias = "#exa:matrix.org",
|
||||
memberCount = null,
|
||||
isDirect = false,
|
||||
topic = null,
|
||||
roomAvatarUrl = null
|
||||
)
|
||||
),
|
||||
|
||||
@@ -203,7 +203,11 @@ private fun JoinRoomTopBar(
|
||||
when (asyncRoomInfo) {
|
||||
is AsyncData.Success -> {
|
||||
val roomInfo = asyncRoomInfo.data
|
||||
RoomAvatarAndNameRow(roomName = roomInfo.roomName, roomAvatar = roomInfo.avatarData(AvatarSize.TimelineRoom))
|
||||
if(roomInfo.roomName == null){
|
||||
IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp)
|
||||
}else {
|
||||
RoomAvatarAndNameRow(roomName = roomInfo.roomName, roomAvatar = roomInfo.avatarData(AvatarSize.TimelineRoom))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp)
|
||||
|
||||
@@ -40,7 +40,6 @@ object JoinRoomModule {
|
||||
return JoinRoomPresenter(
|
||||
roomId = roomId,
|
||||
matrixClient = client,
|
||||
roomListService = roomListService,
|
||||
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class RoomListDataSource @Inject constructor(
|
||||
fun launchIn(coroutineScope: CoroutineScope) {
|
||||
roomListService
|
||||
.allRooms
|
||||
.summaries
|
||||
.filteredSummaries
|
||||
.onEach { roomSummaries ->
|
||||
replaceWith(roomSummaries)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class RoomListSearchDataSource @Inject constructor(
|
||||
source = RoomList.Source.All,
|
||||
)
|
||||
|
||||
val roomSummaries: Flow<PersistentList<RoomListRoomSummary>> = roomList.summaries
|
||||
val roomSummaries: Flow<PersistentList<RoomListRoomSummary>> = roomList.filteredSummaries
|
||||
.map { roomSummaries ->
|
||||
roomSummaries
|
||||
.filterIsInstance<RoomSummary.Filled>()
|
||||
|
||||
@@ -27,7 +27,9 @@ import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
@@ -37,8 +39,10 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.Closeable
|
||||
import java.util.Optional
|
||||
|
||||
interface MatrixClient : Closeable {
|
||||
val sessionId: SessionId
|
||||
@@ -89,6 +93,7 @@ interface MatrixClient : Closeable {
|
||||
suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?>
|
||||
suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String>
|
||||
fun roomMembershipObserver(): RoomMembershipObserver
|
||||
fun getRoomInfoFlow(roomId: RoomId): Flow<Optional<MatrixRoomInfo>>
|
||||
|
||||
fun isMe(userId: UserId?) = userId == sessionId
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.element.android.libraries.matrix.api.roomlist
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -32,6 +33,8 @@ interface DynamicRoomList : RoomList {
|
||||
val loadedPages: StateFlow<Int>
|
||||
val pageSize: Int
|
||||
|
||||
val filteredSummaries: SharedFlow<List<RoomSummary>>
|
||||
|
||||
/**
|
||||
* Load more rooms into the list if possible.
|
||||
*/
|
||||
|
||||
@@ -17,15 +17,9 @@
|
||||
package io.element.android.libraries.matrix.api.roomlist
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import java.util.Optional
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
|
||||
/**
|
||||
* Entry point for the room list api.
|
||||
@@ -86,10 +80,10 @@ interface RoomListService {
|
||||
*/
|
||||
val state: StateFlow<State>
|
||||
|
||||
/**
|
||||
* Get a flow of the room summary for a given room id.
|
||||
*/
|
||||
fun getUserMembershipForRoom(roomId: RoomId): Flow<Optional<CurrentUserMembership>>
|
||||
}
|
||||
|
||||
fun RoomList.loadedStateFlow(): Flow<RoomList.LoadingState.Loaded> {
|
||||
return loadingState.filterIsInstance()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification
|
||||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
@@ -79,12 +80,15 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -104,8 +108,10 @@ import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.INFINITE
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import org.matrix.rustcomponents.sdk.CreateRoomParameters as RustCreateRoomParameters
|
||||
import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset
|
||||
@@ -524,6 +530,22 @@ class RustMatrixClient(
|
||||
|
||||
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
|
||||
|
||||
override fun getRoomInfoFlow(roomId: RoomId): Flow<Optional<MatrixRoomInfo>> {
|
||||
return flow {
|
||||
var room = getRoom(roomId)
|
||||
if (room == null) {
|
||||
emit(Optional.empty())
|
||||
awaitRoom(roomId, INFINITE)
|
||||
room = getRoom(roomId)
|
||||
}
|
||||
room?.use {
|
||||
room.roomInfoFlow
|
||||
.map { roomInfo -> Optional.of(roomInfo) }
|
||||
.collect(this)
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private suspend fun File.getCacheSize(
|
||||
includeCryptoDb: Boolean = false,
|
||||
): Long = withContext(sessionDispatcher) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -92,7 +93,8 @@ internal class RoomListFactory(
|
||||
innerRoomList?.destroy()
|
||||
}
|
||||
return RustDynamicRoomList(
|
||||
summaries = filteredSummariesFlow,
|
||||
summaries = summariesFlow,
|
||||
filteredSummaries = filteredSummariesFlow,
|
||||
loadingState = loadingStateFlow,
|
||||
currentFilter = currentFilter,
|
||||
loadedPages = loadedPages,
|
||||
@@ -105,6 +107,7 @@ internal class RoomListFactory(
|
||||
|
||||
private class RustDynamicRoomList(
|
||||
override val summaries: MutableSharedFlow<List<RoomSummary>>,
|
||||
override val filteredSummaries: SharedFlow<List<RoomSummary>>,
|
||||
override val loadingState: MutableStateFlow<RoomList.LoadingState>,
|
||||
override val currentFilter: MutableStateFlow<RoomListFilter>,
|
||||
override val loadedPages: MutableStateFlow<Int>,
|
||||
|
||||
@@ -16,24 +16,16 @@
|
||||
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
||||
import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally
|
||||
import io.element.android.libraries.matrix.impl.room.map
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -43,9 +35,7 @@ import org.matrix.rustcomponents.sdk.RoomListInput
|
||||
import org.matrix.rustcomponents.sdk.RoomListRange
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.util.Optional
|
||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
|
||||
|
||||
private const val DEFAULT_PAGE_SIZE = 20
|
||||
@@ -122,24 +112,6 @@ internal class RustRoomListService(
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
|
||||
|
||||
override fun getUserMembershipForRoom(roomId: RoomId): Flow<Optional<CurrentUserMembership>> {
|
||||
return combine(
|
||||
allRooms.loadedStateFlow(),
|
||||
invites.loadedStateFlow(),
|
||||
) { _, _ ->
|
||||
val membership = innerRoomListService.roomOrNull(roomId.value)?.use {
|
||||
it.roomInfo().use { roomInfo ->
|
||||
roomInfo.membership.map()
|
||||
}
|
||||
}
|
||||
Optional.ofNullable(membership)
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun RoomList.loadedStateFlow(): Flow<RoomList.LoadingState.Loaded> {
|
||||
return loadingState.filterIsInstance()
|
||||
}
|
||||
}
|
||||
|
||||
private fun RoomListServiceState.toRoomListState(): RoomListService.State {
|
||||
|
||||
Reference in New Issue
Block a user