diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 840308af23..82ea24e7ff 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.room.draft.ComposerDraft +import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange @@ -56,6 +57,11 @@ interface MatrixRoom : Closeable { val roomTypingMembersFlow: Flow> val identityStateChangesFlow: Flow> + /** + * The current knock requests in the room as a Flow. + */ + val knockRequestsFlow: Flow> + /** * A one-to-one is a room with exactly 2 members. * See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules). diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/knock/KnockRequest.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/knock/KnockRequest.kt new file mode 100644 index 0000000000..f5b25b5f2c --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/knock/KnockRequest.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.room.knock + +import io.element.android.libraries.matrix.api.core.UserId + +interface KnockRequest { + val userId: UserId + val displayName: String? + val avatarUrl: String? + val reason: String? + val timestamp: Long? + + suspend fun accept(): Result + + suspend fun decline(reason: String?): Result + + suspend fun declineAndBan(reason: String?): Result + + suspend fun markAsSeen(): Result +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index c84a37cdc3..44f588bd83 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -38,6 +38,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.draft.ComposerDraft +import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange @@ -50,6 +51,7 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.impl.core.RustSendHandle import io.element.android.libraries.matrix.impl.mapper.map import io.element.android.libraries.matrix.impl.room.draft.into +import io.element.android.libraries.matrix.impl.room.knock.RustKnockRequest import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper @@ -76,6 +78,8 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.DateDividerMode import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener +import org.matrix.rustcomponents.sdk.JoinRequest +import org.matrix.rustcomponents.sdk.RequestsToJoinListener import org.matrix.rustcomponents.sdk.RoomInfo import org.matrix.rustcomponents.sdk.RoomInfoListener import org.matrix.rustcomponents.sdk.RoomListItem @@ -157,6 +161,15 @@ class RustMatrixRoom( }) } + override val knockRequestsFlow: Flow> = mxCallbackFlow { + innerRoom.subscribeToJoinRequests(object : RequestsToJoinListener { + override fun call(joinRequests: List) { + val knockRequests = joinRequests.map { RustKnockRequest(it) } + channel.trySend(knockRequests) + } + }) + } + // Create a dispatcher for all room methods... private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/knock/RustKnockRequest.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/knock/RustKnockRequest.kt new file mode 100644 index 0000000000..a3d133d599 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/knock/RustKnockRequest.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.room.knock + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.knock.KnockRequest +import org.matrix.rustcomponents.sdk.JoinRequest + +class RustKnockRequest( + private val inner: JoinRequest, +) : KnockRequest { + override val userId: UserId = UserId(inner.userId) + override val displayName: String? = inner.displayName + override val avatarUrl: String? = inner.avatarUrl + override val reason: String? = inner.reason + override val timestamp: Long? = inner.timestamp?.toLong() + + override suspend fun accept(): Result = runCatching { + inner.actions.accept() + } + + override suspend fun decline(reason: String?): Result = runCatching { + inner.actions.decline(reason) + } + + override suspend fun declineAndBan(reason: String?): Result = runCatching { + inner.actions.declineAndBan(reason) + } + + override suspend fun markAsSeen(): Result = runCatching { + inner.actions.markAsSeen() + } +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 41d913b89c..cfb73e267e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.draft.ComposerDraft +import io.element.android.libraries.matrix.api.room.knock.KnockRequest import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange @@ -163,6 +164,13 @@ class FakeMatrixRoom( _identityStateChangesFlow.tryEmit(identityStateChanges) } + private val _knockRequestsFlow: MutableSharedFlow> = MutableSharedFlow(replay = 1) + override val knockRequestsFlow: Flow> = _knockRequestsFlow + + fun emitKnockRequests(knockRequests: List) { + _knockRequestsFlow.tryEmit(knockRequests) + } + override val membersStateFlow: MutableStateFlow = MutableStateFlow(MatrixRoomMembersState.Unknown) override val roomNotificationSettingsStateFlow: MutableStateFlow = diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/knock/FakeKnockRequest.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/knock/FakeKnockRequest.kt new file mode 100644 index 0000000000..fad12e6bb6 --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/knock/FakeKnockRequest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.matrix.test.room.knock + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.knock.KnockRequest +import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeKnockRequest( + override val userId: UserId = A_USER_ID, + override val displayName: String? = A_USER_NAME, + override val avatarUrl: String? = AN_AVATAR_URL, + override val reason: String? = null, + override val timestamp: Long? = null, + val acceptLambda: () -> Result = { lambdaError() }, + val declineLambda: (String?) -> Result = { lambdaError() }, + val declineAndBanLambda: (String?) -> Result = { lambdaError() }, + val markAsSeenLambda: () -> Result = { lambdaError() }, +) : KnockRequest { + override suspend fun accept(): Result { + return acceptLambda() + } + + override suspend fun decline(reason: String?): Result { + return declineLambda(reason) + } + + override suspend fun declineAndBan(reason: String?): Result { + return declineAndBanLambda(reason) + } + + override suspend fun markAsSeen(): Result { + return markAsSeenLambda() + } +}