[Room Details] Block & unblock user (#340)
This commit is contained in:
parent
0d327cd543
commit
aef2b38983
35 changed files with 477 additions and 174 deletions
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.core.coroutine
|
||||
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
/** Create a Flow emitting a single error event. It should be useful for tests. */
|
||||
fun <T> errorFlow(throwable: Throwable) = flow<T> { throw throwable }
|
||||
|
|
@ -36,13 +36,13 @@ interface MatrixRoom : Closeable {
|
|||
val isDirect: Boolean
|
||||
val isPublic: Boolean
|
||||
|
||||
suspend fun members(): List<RoomMember>
|
||||
fun members() : Flow<List<RoomMember>>
|
||||
|
||||
suspend fun memberCount(): Int
|
||||
fun updateMembers()
|
||||
|
||||
fun getMember(userId: UserId): RoomMember?
|
||||
fun getMember(userId: UserId): Flow<RoomMember?>
|
||||
|
||||
fun getDmMember(): RoomMember?
|
||||
fun getDmMember(): Flow<RoomMember?>
|
||||
|
||||
fun syncUpdateFlow(): Flow<Long>
|
||||
|
||||
|
|
@ -62,6 +62,10 @@ interface MatrixRoom : Closeable {
|
|||
|
||||
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
|
||||
|
||||
suspend fun ignoreUser(userId: UserId): Result<Unit>
|
||||
|
||||
suspend fun unignoreUser(userId: UserId): Result<Unit>
|
||||
|
||||
suspend fun leave(): Result<Unit>
|
||||
|
||||
suspend fun acceptInvitation(): Result<Unit>
|
||||
|
|
|
|||
|
|
@ -26,18 +26,19 @@ import io.element.android.libraries.matrix.api.room.RoomMember
|
|||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.onSubscription
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncRoom
|
||||
import org.matrix.rustcomponents.sdk.UpdateSummary
|
||||
import org.matrix.rustcomponents.sdk.genTransactionId
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||
import org.matrix.rustcomponents.sdk.RoomMember as RustRoomMember
|
||||
|
||||
class RustMatrixRoom(
|
||||
private val currentUserId: UserId,
|
||||
|
|
@ -48,37 +49,40 @@ class RustMatrixRoom(
|
|||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
) : MatrixRoom {
|
||||
|
||||
private var loadMembersJob: Job? = null
|
||||
private var cachedMembers: List<RoomMember> = emptyList()
|
||||
private val timeline by lazy {
|
||||
RustMatrixTimeline(
|
||||
matrixRoom = this,
|
||||
innerRoom = innerRoom,
|
||||
slidingSyncRoom = slidingSyncRoom,
|
||||
coroutineScope = coroutineScope,
|
||||
coroutineDispatchers = coroutineDispatchers
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun members(): List<RoomMember> {
|
||||
return cachedMembers.ifEmpty {
|
||||
if (loadMembersJob == null) {
|
||||
loadMembersJob = coroutineScope.launch(coroutineDispatchers.io) {
|
||||
cachedMembers = tryOrNull {
|
||||
innerRoom.members().map(RoomMemberMapper::map)
|
||||
} ?: emptyList()
|
||||
}
|
||||
private var membersFlow = MutableStateFlow<List<RoomMember>>(emptyList())
|
||||
|
||||
override fun members(): Flow<List<RoomMember>> {
|
||||
return membersFlow.onSubscription { updateMembers() }
|
||||
}
|
||||
|
||||
override fun updateMembers() {
|
||||
val updatedMembers = tryOrNull {
|
||||
innerRoom.members().map(RoomMemberMapper::map)
|
||||
} ?: emptyList()
|
||||
membersFlow.tryEmit(updatedMembers)
|
||||
}
|
||||
|
||||
override fun getMember(userId: UserId): Flow<RoomMember?> {
|
||||
return membersFlow.map { members -> members.find { it.userId == userId } }
|
||||
}
|
||||
|
||||
override fun getDmMember(): Flow<RoomMember?> {
|
||||
return membersFlow.map { members ->
|
||||
if (members.size == 2 && isDirect && isEncrypted) {
|
||||
members.find { it.userId != currentUserId }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
loadMembersJob?.join()
|
||||
loadMembersJob = null
|
||||
cachedMembers
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun memberCount(): Int {
|
||||
return members().size
|
||||
}
|
||||
|
||||
override fun getMember(userId: UserId): RoomMember? {
|
||||
return cachedMembers.find { it.userId == userId }
|
||||
}
|
||||
|
||||
override fun getDmMember(): RoomMember? {
|
||||
return if (cachedMembers.size == 2 && isDirect && isEncrypted) {
|
||||
cachedMembers.find { it.userId != currentUserId }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,13 +98,7 @@ class RustMatrixRoom(
|
|||
}
|
||||
|
||||
override fun timeline(): MatrixTimeline {
|
||||
return RustMatrixTimeline(
|
||||
matrixRoom = this,
|
||||
innerRoom = innerRoom,
|
||||
slidingSyncRoom = slidingSyncRoom,
|
||||
coroutineScope = coroutineScope,
|
||||
coroutineDispatchers = coroutineDispatchers
|
||||
)
|
||||
return timeline
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
|
|
@ -219,4 +217,20 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override suspend fun ignoreUser(userId: UserId): Result<Unit> {
|
||||
return runCatching {
|
||||
getRustMember(userId)?.ignore() ?: error("No member with userId $userId exists in room $roomId")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun unignoreUser(userId: UserId): Result<Unit> {
|
||||
return runCatching {
|
||||
getRustMember(userId)?.unignore() ?: error("No member with userId $userId exists in room $roomId")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRustMember(userId: UserId): RustRoomMember? {
|
||||
return innerRoom.members().find { it.userId() == userId.value }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,14 @@ class RustMatrixTimeline(
|
|||
|
||||
private suspend fun addListener(timelineListener: TimelineListener): Result<List<TimelineItem>> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val settings = RoomSubscription(requiredState = listOf(RequiredState(key = "m.room.canonical_alias", value = "")), timelineLimit = null)
|
||||
val settings = RoomSubscription(
|
||||
requiredState = listOf(
|
||||
RequiredState(key = "m.room.topic", value = ""),
|
||||
RequiredState(key = "m.room.canonical_alias", value = ""),
|
||||
RequiredState(key = "m.room.join_rules", value = ""),
|
||||
),
|
||||
timelineLimit = null
|
||||
)
|
||||
val result = slidingSyncRoom.subscribeAndAddTimelineListener(timelineListener, settings)
|
||||
listenerTokens += result.taskHandle
|
||||
result.items
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.libraries.core)
|
||||
api(projects.libraries.matrix.api)
|
||||
api(libs.coroutines.core)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.core.coroutine.errorFlow
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
|
@ -27,6 +28,7 @@ import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class FakeMatrixRoom(
|
||||
override val roomId: RoomId = A_ROOM_ID,
|
||||
|
|
@ -50,6 +52,8 @@ class FakeMatrixRoom(
|
|||
private var rejectInviteResult = Result.success(Unit)
|
||||
private var dmMember: RoomMember? = null
|
||||
private var fetchMemberResult: Result<Unit> = Result.success(Unit)
|
||||
private var ignoreResult = Result.success(Unit)
|
||||
private var unignoreResult = Result.success(Unit)
|
||||
|
||||
var areMembersFetched: Boolean = false
|
||||
private set
|
||||
|
|
@ -78,8 +82,8 @@ class FakeMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getDmMember(): RoomMember? {
|
||||
return dmMember
|
||||
override fun getDmMember(): Flow<RoomMember?> {
|
||||
return flowOf(dmMember)
|
||||
}
|
||||
|
||||
override suspend fun userDisplayName(userId: UserId): Result<String?> {
|
||||
|
|
@ -90,20 +94,18 @@ class FakeMatrixRoom(
|
|||
return userAvatarUrlResult
|
||||
}
|
||||
|
||||
override suspend fun members(): List<RoomMember> {
|
||||
return members
|
||||
override fun members(): Flow<List<RoomMember>> {
|
||||
return fetchMemberResult.fold(onSuccess = {
|
||||
flowOf(members)
|
||||
}, onFailure = {
|
||||
errorFlow(it)
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun memberCount(): Int {
|
||||
if (fetchMemberResult.isSuccess) {
|
||||
return members.count()
|
||||
} else {
|
||||
throw fetchMemberResult.exceptionOrNull()!!
|
||||
}
|
||||
}
|
||||
override fun updateMembers() = Unit
|
||||
|
||||
override fun getMember(userId: UserId): RoomMember? {
|
||||
return members.firstOrNull { it.userId == userId }
|
||||
override fun getMember(userId: UserId): Flow<RoomMember?> {
|
||||
return flowOf(members.find { it.userId == userId })
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: String): Result<Unit> {
|
||||
|
|
@ -138,6 +140,10 @@ class FakeMatrixRoom(
|
|||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun ignoreUser(userId: UserId): Result<Unit> = ignoreResult
|
||||
|
||||
override suspend fun unignoreUser(userId: UserId): Result<Unit> = unignoreResult
|
||||
|
||||
override suspend fun leave(): Result<Unit> = leaveRoomError?.let { Result.failure(it) } ?: Result.success(Unit)
|
||||
override suspend fun acceptInvitation(): Result<Unit> {
|
||||
isInviteAccepted = true
|
||||
|
|
@ -179,4 +185,12 @@ class FakeMatrixRoom(
|
|||
rejectInviteResult = result
|
||||
}
|
||||
|
||||
|
||||
fun givenIgnoreResult(result: Result<Unit>) {
|
||||
ignoreResult = result
|
||||
}
|
||||
|
||||
fun givenUnIgnoreResult(result: Result<Unit>) {
|
||||
unignoreResult = result
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue