Make MatrixClient return a RoomPreview instance, not a RoomPreviewInfo one.
This commit is contained in:
committed by
Jorge Martin Espinosa
parent
19fcd005fc
commit
311fbb84f7
@@ -77,9 +77,11 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val declineInviteFailure = lambdaRecorder { ->
|
||||
Result.failure<Unit>(RuntimeException("Failed to leave room"))
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
getRoomPreviewResults[A_ROOM_ID] = FakeRoomPreview(declineInviteResult = declineInviteFailure)
|
||||
}
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(FakeRoomPreview(declineInviteResult = declineInviteFailure))
|
||||
}
|
||||
)
|
||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
@@ -120,9 +122,11 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val declineInviteSuccess = lambdaRecorder { ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
getRoomPreviewResults[A_ROOM_ID] = FakeRoomPreview(declineInviteResult = declineInviteSuccess)
|
||||
}
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(FakeRoomPreview(declineInviteResult = declineInviteSuccess))
|
||||
}
|
||||
)
|
||||
val presenter = createAcceptDeclineInvitePresenter(
|
||||
client = client,
|
||||
notificationCleaner = fakeNotificationCleaner,
|
||||
|
||||
@@ -104,10 +104,12 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||
}
|
||||
else -> {
|
||||
value = ContentState.Loading
|
||||
val result = matrixClient.getRoomPreviewInfo(roomIdOrAlias, serverNames)
|
||||
val result = matrixClient.getRoomPreview(roomIdOrAlias, serverNames)
|
||||
value = result.fold(
|
||||
onSuccess = { previewInfo ->
|
||||
previewInfo.toContentState()
|
||||
onSuccess = { preview ->
|
||||
preview.info.toContentState()
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
if (throwable is ClientException.MatrixApi && (throwable.kind == ErrorKind.NotFound || throwable.kind == ErrorKind.Forbidden)) {
|
||||
|
||||
@@ -257,6 +257,19 @@ class JoinRoomPresenterTest {
|
||||
fun `present - when room is banned, then join authorization is equal to IsBanned`() = runTest {
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.BANNED, joinRule = JoinRule.Public)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = CurrentUserMembership.BANNED,
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
).apply {
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
@@ -504,19 +517,21 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = RoomAlias("#alias:matrix.org"),
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 2,
|
||||
isSpace = false,
|
||||
isHistoryWorldReadable = false,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = null,
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = RoomAlias("#alias:matrix.org"),
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 2,
|
||||
isSpace = false,
|
||||
isHistoryWorldReadable = false,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = null,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -547,9 +562,9 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Private`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreviewInfo(joinRule = JoinRule.Private)
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Private))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -567,9 +582,9 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Custom`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreviewInfo(joinRule = JoinRule.Custom("custom"))
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Custom("custom")))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -587,9 +602,9 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Invite`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreviewInfo(joinRule = JoinRule.Invite)
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Invite))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -607,9 +622,9 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as KnockRestricted`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreviewInfo(joinRule = JoinRule.KnockRestricted(emptyList()))
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.KnockRestricted(emptyList())))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -627,9 +642,9 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Restricted`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreviewInfo(joinRule = JoinRule.Restricted(emptyList()))
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Restricted(emptyList())))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -647,9 +662,9 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Space`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreviewInfo(isSpace = true)
|
||||
aRoomPreview(info = aRoomPreviewInfo(isSpace = true))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -667,7 +682,7 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded with error`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.failure(AN_EXCEPTION)
|
||||
}
|
||||
)
|
||||
@@ -697,7 +712,7 @@ class JoinRoomPresenterTest {
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded with error Forbidden`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden"))
|
||||
}
|
||||
)
|
||||
|
||||
@@ -28,7 +28,6 @@ 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.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
@@ -146,7 +145,11 @@ interface MatrixClient : Closeable {
|
||||
* Execute generic GET requests through the SDKs internal HTTP client.
|
||||
*/
|
||||
suspend fun getUrl(url: String): Result<String>
|
||||
suspend fun getRoomPreviewInfo(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreviewInfo>
|
||||
|
||||
/**
|
||||
* Get a room preview for a given room ID or alias. This is especially useful for rooms that the user is not a member of, or hasn't joined yet.
|
||||
*/
|
||||
suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreview>
|
||||
|
||||
/**
|
||||
* Returns the currently used sliding sync version.
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
package io.element.android.libraries.matrix.api.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
|
||||
/** A reference to a room either invited, knocked or banned. */
|
||||
interface RoomPreview : AutoCloseable {
|
||||
val sessionId: SessionId
|
||||
val roomId: RoomId
|
||||
val info: RoomPreviewInfo
|
||||
|
||||
/** Leave the room ie.decline invite or cancel knock. */
|
||||
suspend fun leave(): Result<Unit>
|
||||
|
||||
@@ -38,7 +38,6 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
@@ -60,9 +59,9 @@ import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomFactory
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomPreview
|
||||
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
|
||||
import io.element.android.libraries.matrix.impl.room.join.map
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.map
|
||||
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
|
||||
@@ -450,14 +449,13 @@ class RustMatrixClient(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getRoomPreviewInfo(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreviewInfo> = withContext(sessionDispatcher) {
|
||||
override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreview> = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
when (roomIdOrAlias) {
|
||||
val roomPreview = when (roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> innerClient.getRoomPreviewFromRoomAlias(roomIdOrAlias.roomAlias.value)
|
||||
is RoomIdOrAlias.Id -> innerClient.getRoomPreviewFromRoomId(roomIdOrAlias.roomId.value, serverNames)
|
||||
}.use { roomPreview ->
|
||||
RoomPreviewInfoMapper.map(roomPreview.info())
|
||||
}
|
||||
RustRoomPreview(sessionId, roomPreview, roomMembershipObserver)
|
||||
}.mapFailure { it.mapClientException() }
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,6 @@ class RustRoomFactory(
|
||||
}
|
||||
RustRoomPreview(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
inner = innerRoom,
|
||||
roomMembershipObserver = roomMembershipObserver,
|
||||
)
|
||||
|
||||
@@ -7,27 +7,33 @@
|
||||
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.RoomPreview as InnerRoomPreview
|
||||
|
||||
@Immutable
|
||||
class RustRoomPreview(
|
||||
override val sessionId: SessionId,
|
||||
override val roomId: RoomId,
|
||||
private val inner: InnerRoomPreview,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
private val roomMembershipObserver: RoomMembershipObserver?,
|
||||
) : RoomPreview {
|
||||
companion object {
|
||||
val ALLOWED_MEMBERSHIPS = setOf(Membership.INVITED, Membership.KNOCKED, Membership.BANNED)
|
||||
}
|
||||
|
||||
override val info: RoomPreviewInfo = RoomPreviewInfoMapper.map(inner.info())
|
||||
|
||||
override suspend fun leave(): Result<Unit> = runCatching {
|
||||
inner.leave()
|
||||
}.onSuccess {
|
||||
roomMembershipObserver.notifyUserLeftRoom(roomId)
|
||||
roomMembershipObserver?.notifyUserLeftRoom(info.roomId)
|
||||
}
|
||||
|
||||
override suspend fun forget(): Result<Unit> = runCatching {
|
||||
|
||||
@@ -26,7 +26,6 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
@@ -79,7 +78,7 @@ class FakeMatrixClient(
|
||||
Optional.of(ResolvedRoomAlias(A_ROOM_ID, emptyList()))
|
||||
)
|
||||
},
|
||||
private val getRoomPreviewInfoResult: (RoomIdOrAlias, List<String>) -> Result<RoomPreviewInfo> = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
private val getRoomPreviewResult: (RoomIdOrAlias, List<String>) -> Result<RoomPreview> = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
private val clearCacheLambda: () -> Unit = { lambdaError() },
|
||||
private val userIdServerNameLambda: () -> String = { lambdaError() },
|
||||
private val getUrlLambda: (String) -> Result<String> = { lambdaError() },
|
||||
@@ -105,7 +104,6 @@ class FakeMatrixClient(
|
||||
private var createDmResult: Result<RoomId> = Result.success(A_ROOM_ID)
|
||||
private var findDmResult: RoomId? = A_ROOM_ID
|
||||
private val getRoomResults = mutableMapOf<RoomId, MatrixRoom>()
|
||||
val getRoomPreviewResults = mutableMapOf<RoomId, RoomPreview>()
|
||||
private val searchUserResults = mutableMapOf<String, Result<MatrixSearchUserResults>>()
|
||||
private val getProfileResults = mutableMapOf<UserId, Result<MatrixUser>>()
|
||||
private var uploadMediaResult: Result<String> = Result.success(AN_AVATAR_URL)
|
||||
@@ -132,8 +130,8 @@ class FakeMatrixClient(
|
||||
return getRoomResults[roomId]
|
||||
}
|
||||
|
||||
override suspend fun getPendingRoom(roomId: RoomId): RoomPreview? {
|
||||
return getRoomPreviewResults[roomId]
|
||||
override suspend fun getPendingRoom(roomId: RoomId): RoomPreview? = simulateLongTask {
|
||||
getRoomPreviewResult(RoomIdOrAlias.Id(roomId), emptyList()).getOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findDM(userId: UserId): RoomId? {
|
||||
@@ -313,8 +311,8 @@ class FakeMatrixClient(
|
||||
resolveRoomAliasResult(roomAlias)
|
||||
}
|
||||
|
||||
override suspend fun getRoomPreviewInfo(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreviewInfo> = simulateLongTask {
|
||||
getRoomPreviewInfoResult(roomIdOrAlias, serverNames)
|
||||
override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreview> = simulateLongTask {
|
||||
getRoomPreviewResult(roomIdOrAlias, serverNames)
|
||||
}
|
||||
|
||||
override suspend fun getRecentlyVisitedRooms(): Result<List<RoomId>> {
|
||||
|
||||
@@ -7,17 +7,19 @@
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
@Immutable
|
||||
class FakeRoomPreview(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val roomId: RoomId = A_ROOM_ID,
|
||||
override val info: RoomPreviewInfo = aRoomPreviewInfo(),
|
||||
private val declineInviteResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val forgetRoomResult: () -> Result<Unit> = { lambdaError() },
|
||||
) : RoomPreview {
|
||||
|
||||
@@ -9,7 +9,9 @@ package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
@@ -17,6 +19,20 @@ import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
fun aRoomPreview(
|
||||
sessionId: SessionId = A_SESSION_ID,
|
||||
info: RoomPreviewInfo = aRoomPreviewInfo(),
|
||||
declineInviteResult: () -> Result<Unit> = { lambdaError() },
|
||||
forgetRoomResult: () -> Result<Unit> = { lambdaError() },
|
||||
) = FakeRoomPreview(
|
||||
sessionId = sessionId,
|
||||
info = info,
|
||||
declineInviteResult = declineInviteResult,
|
||||
forgetRoomResult = forgetRoomResult,
|
||||
)
|
||||
|
||||
fun aRoomPreviewInfo(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
|
||||
Reference in New Issue
Block a user