Favorite : rework and add tests
This commit is contained in:
parent
b15597509d
commit
d9017a098c
21 changed files with 454 additions and 144 deletions
27
features/roomactions/api/build.gradle.kts
Normal file
27
features/roomactions/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.roomactions.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.features.roomactions.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
|
||||
/**
|
||||
* Set the favorite status of a room.
|
||||
* This will update the notable tags of the room.
|
||||
*/
|
||||
interface SetRoomIsFavoriteAction {
|
||||
sealed interface Result {
|
||||
data object Success : Result
|
||||
data object RoomNotFound : Result
|
||||
data class Exception(val inner: java.lang.Exception) : Result
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the favorite status of a room by its id, it'll try to load the room from the session.
|
||||
*/
|
||||
suspend operator fun invoke(roomId: RoomId, isFavorite: Boolean): Result
|
||||
|
||||
/**
|
||||
* Set the favorite status of a room using the provided instance.
|
||||
*/
|
||||
suspend operator fun invoke(room: MatrixRoom, isFavorite: Boolean): Result
|
||||
}
|
||||
50
features/roomactions/impl/build.gradle.kts
Normal file
50
features/roomactions/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.
|
||||
*/
|
||||
|
||||
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
|
||||
@Suppress("DSL_SCOPE_VIOLATION")
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
alias(libs.plugins.anvil)
|
||||
alias(libs.plugins.ksp)
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.roomactions.impl"
|
||||
}
|
||||
|
||||
anvil {
|
||||
generateDaggerFactories.set(true)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.anvilannotations)
|
||||
anvil(projects.anvilcodegen)
|
||||
api(projects.features.roomactions.api)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.molecule.runtime)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
|
||||
ksp(libs.showkase.processor)
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.features.roomactions.impl
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.features.roomactions.api.SetRoomIsFavoriteAction
|
||||
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.MatrixRoom
|
||||
import kotlinx.coroutines.flow.first
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultSetRoomIsFavoriteAction @Inject constructor(private val client: MatrixClient) : SetRoomIsFavoriteAction {
|
||||
override suspend operator fun invoke(roomId: RoomId, isFavorite: Boolean): SetRoomIsFavoriteAction.Result {
|
||||
return client.getRoom(roomId).use { room ->
|
||||
room?.setIsFavorite(isFavorite) ?: SetRoomIsFavoriteAction.Result.RoomNotFound
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun invoke(room: MatrixRoom, isFavorite: Boolean): SetRoomIsFavoriteAction.Result {
|
||||
return room.setIsFavorite(isFavorite)
|
||||
}
|
||||
|
||||
private suspend fun MatrixRoom.setIsFavorite(isFavorite: Boolean): SetRoomIsFavoriteAction.Result {
|
||||
val notableTags = notableTagsFlow.first().copy(isFavorite = isFavorite)
|
||||
return updateNotableTags(notableTags).fold(
|
||||
onSuccess = {
|
||||
SetRoomIsFavoriteAction.Result.Success
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
if (throwable is Exception && throwable !is CancellationException) {
|
||||
SetRoomIsFavoriteAction.Result.Exception(throwable)
|
||||
} else {
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.features.roomactions.impl
|
||||
|
||||
import io.element.android.features.roomactions.api.SetRoomIsFavoriteAction
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultSetRoomIsFavoriteActionTests {
|
||||
private val room = FakeMatrixRoom()
|
||||
|
||||
@Test
|
||||
fun `given a room id and a client without rooms, when action is invoked, then it returns Result_RoomNotFound`() = runTest {
|
||||
val action = DefaultSetRoomIsFavoriteAction(FakeMatrixClient())
|
||||
val result = action(room.roomId, true)
|
||||
assert(result is SetRoomIsFavoriteAction.Result.RoomNotFound)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a room, when action is invoked, then it returns Result_Success`() = runTest {
|
||||
val action = DefaultSetRoomIsFavoriteAction(FakeMatrixClient())
|
||||
val result = action(room, true)
|
||||
assert(result is SetRoomIsFavoriteAction.Result.Success)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a room id and a client with a room, when action is invoked, then it returns Result_Success`() = runTest {
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(room.roomId, room)
|
||||
}
|
||||
val action = DefaultSetRoomIsFavoriteAction(client)
|
||||
val result = action(room.roomId, true)
|
||||
assert(result is SetRoomIsFavoriteAction.Result.Success)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a room, when action is invoked and fail, then it returns Result_Exception`() = runTest {
|
||||
val action = DefaultSetRoomIsFavoriteAction(FakeMatrixClient())
|
||||
room.givenUpdateNotableTagsResult(Result.failure(Exception()))
|
||||
val result = action(room, true)
|
||||
assert(result is SetRoomIsFavoriteAction.Result.Exception)
|
||||
}
|
||||
}
|
||||
28
features/roomactions/test/build.gradle.kts
Normal file
28
features/roomactions/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.features.roomactions.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.features.roomactions.api)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(libs.coroutines.core)
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.features.roomactions.test
|
||||
|
||||
import io.element.android.features.roomactions.api.SetRoomIsFavoriteAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
|
||||
class FakeSetRoomIsFavoriteAction : SetRoomIsFavoriteAction {
|
||||
private var counter = 0
|
||||
|
||||
fun assertCalled(number: Int) {
|
||||
assert(counter == number) { "Expected $number calls, got $counter" }
|
||||
}
|
||||
|
||||
override suspend fun invoke(roomId: RoomId, isFavorite: Boolean): SetRoomIsFavoriteAction.Result {
|
||||
counter++
|
||||
return SetRoomIsFavoriteAction.Result.Success
|
||||
}
|
||||
|
||||
override suspend fun invoke(room: MatrixRoom, isFavorite: Boolean): SetRoomIsFavoriteAction.Result {
|
||||
counter++
|
||||
return SetRoomIsFavoriteAction.Result.Success
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ dependencies {
|
|||
implementation(projects.features.createroom.api)
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(projects.features.poll.api)
|
||||
implementation(projects.features.roomactions.api)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.coroutines.test)
|
||||
|
|
@ -70,6 +71,7 @@ dependencies {
|
|||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(projects.features.leaveroom.test)
|
||||
testImplementation(projects.features.createroom.test)
|
||||
testImplementation(projects.features.roomactions.test)
|
||||
|
||||
ksp(libs.showkase.processor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||
import io.element.android.features.roomactions.api.SetRoomIsFavoriteAction
|
||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
|
|
@ -44,7 +45,6 @@ import io.element.android.libraries.matrix.api.room.StateEventType
|
|||
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
|
@ -61,6 +61,7 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
private val roomMembersDetailsPresenterFactory: RoomMemberDetailsPresenter.Factory,
|
||||
private val leaveRoomPresenter: LeaveRoomPresenter,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val setRoomIsFavorite: SetRoomIsFavoriteAction,
|
||||
) : Presenter<RoomDetailsState> {
|
||||
@Composable
|
||||
override fun present(): RoomDetailsState {
|
||||
|
|
@ -126,9 +127,8 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
is RoomDetailsEvent.SetIsFavorite -> {
|
||||
scope.launch(dispatchers.io) {
|
||||
val tags = RoomNotableTags(isFavorite = event.isFavorite)
|
||||
room.updateNotableTags(tags)
|
||||
scope.launch {
|
||||
setRoomIsFavorite(room, event.isFavorite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,18 @@ package io.element.android.features.roomdetails
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.TurbineTestContext
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.createroom.test.FakeStartDMAction
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||
import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter
|
||||
import io.element.android.features.roomactions.api.SetRoomIsFavoriteAction
|
||||
import io.element.android.features.roomactions.test.FakeSetRoomIsFavoriteAction
|
||||
import io.element.android.features.roomdetails.impl.RoomDetailsEvent
|
||||
import io.element.android.features.roomdetails.impl.RoomDetailsPresenter
|
||||
import io.element.android.features.roomdetails.impl.RoomDetailsState
|
||||
import io.element.android.features.roomdetails.impl.RoomDetailsType
|
||||
import io.element.android.features.roomdetails.impl.RoomTopicState
|
||||
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
||||
|
|
@ -40,6 +44,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
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_SESSION_ID
|
||||
|
|
@ -71,10 +76,11 @@ class RoomDetailsPresenterTests {
|
|||
}
|
||||
|
||||
private fun TestScope.createRoomDetailsPresenter(
|
||||
room: MatrixRoom,
|
||||
room: MatrixRoom = aMatrixRoom(),
|
||||
leaveRoomPresenter: LeaveRoomPresenter = FakeLeaveRoomPresenter(),
|
||||
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService()
|
||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
setRoomIsFavoriteAction: SetRoomIsFavoriteAction = FakeSetRoomIsFavoriteAction(),
|
||||
): RoomDetailsPresenter {
|
||||
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
|
||||
val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory {
|
||||
|
|
@ -86,25 +92,30 @@ class RoomDetailsPresenterTests {
|
|||
mapOf(FeatureFlags.NotificationSettings.key to true)
|
||||
)
|
||||
return RoomDetailsPresenter(
|
||||
matrixClient,
|
||||
room,
|
||||
featureFlagService,
|
||||
matrixClient.notificationSettingsService(),
|
||||
roomMemberDetailsPresenterFactory,
|
||||
leaveRoomPresenter,
|
||||
dispatchers
|
||||
client = matrixClient,
|
||||
room = room,
|
||||
featureFlagService = featureFlagService,
|
||||
notificationSettingsService = matrixClient.notificationSettingsService(),
|
||||
roomMembersDetailsPresenterFactory = roomMemberDetailsPresenterFactory,
|
||||
leaveRoomPresenter = leaveRoomPresenter,
|
||||
dispatchers = dispatchers,
|
||||
setRoomIsFavorite = setRoomIsFavoriteAction,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun RoomDetailsPresenter.test(validate: suspend TurbineTestContext<RoomDetailsState>.() -> Unit) {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
present()
|
||||
}
|
||||
}.test(validate = validate)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state is created from room if roomInfo is null`() = runTest {
|
||||
val room = aMatrixRoom()
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.roomId).isEqualTo(room.roomId.value)
|
||||
assertThat(initialState.roomName).isEqualTo(room.name)
|
||||
|
|
@ -124,11 +135,7 @@ class RoomDetailsPresenterTests {
|
|||
givenRoomInfo(roomInfo)
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
val updatedState = awaitItem()
|
||||
assertThat(updatedState.roomName).isEqualTo(roomInfo.name)
|
||||
|
|
@ -143,11 +150,7 @@ class RoomDetailsPresenterTests {
|
|||
fun `present - initial state with no room name`() = runTest {
|
||||
val room = aMatrixRoom(name = null)
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.roomName).isEqualTo(room.displayName)
|
||||
|
||||
|
|
@ -167,11 +170,7 @@ class RoomDetailsPresenterTests {
|
|||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.roomType).isEqualTo(RoomDetailsType.Dm(otherRoomMember))
|
||||
|
||||
|
|
@ -185,11 +184,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanInviteResult(Result.success(true))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Initially false
|
||||
assertThat(awaitItem().canInvite).isFalse()
|
||||
// Then the asynchronous check completes and it becomes true
|
||||
|
|
@ -205,11 +200,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
assertThat(awaitItem().canInvite).isFalse()
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
|
|
@ -222,11 +213,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanInviteResult(Result.failure(Throwable("Whoops")))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
assertThat(awaitItem().canInvite).isFalse()
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
|
|
@ -242,11 +229,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Initially false
|
||||
assertThat(awaitItem().canEdit).isFalse()
|
||||
// Then the asynchronous check completes and it becomes true
|
||||
|
|
@ -273,11 +256,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Initially false
|
||||
assertThat(awaitItem().canEdit).isFalse()
|
||||
// Then the asynchronous check completes, but editing is still disallowed because it's a DM
|
||||
|
|
@ -305,11 +284,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
|
||||
// There's no topic, so we hide the entire UI for DMs
|
||||
|
|
@ -328,11 +303,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Initially false
|
||||
assertThat(awaitItem().canEdit).isFalse()
|
||||
// Then the asynchronous check completes and it becomes true
|
||||
|
|
@ -351,11 +322,7 @@ class RoomDetailsPresenterTests {
|
|||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Initially false, and no further events
|
||||
assertThat(awaitItem().canEdit).isFalse()
|
||||
|
||||
|
|
@ -371,11 +338,7 @@ class RoomDetailsPresenterTests {
|
|||
}
|
||||
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
// The initial state is "hidden" and no further state changes happen
|
||||
assertThat(awaitItem().roomTopic).isEqualTo(RoomTopicState.Hidden)
|
||||
|
||||
|
|
@ -392,11 +355,7 @@ class RoomDetailsPresenterTests {
|
|||
}
|
||||
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
// Ignore the initial state
|
||||
skipItems(1)
|
||||
|
||||
|
|
@ -416,11 +375,7 @@ class RoomDetailsPresenterTests {
|
|||
leaveRoomPresenter = leaveRoomPresenter,
|
||||
dispatchers = testCoroutineDispatchers()
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
awaitItem().eventSink(RoomDetailsEvent.LeaveRoom)
|
||||
|
||||
assertThat(leaveRoomPresenter.events).contains(LeaveRoomEvent.ShowConfirmation(room.roomId))
|
||||
|
|
@ -439,11 +394,7 @@ class RoomDetailsPresenterTests {
|
|||
leaveRoomPresenter = leaveRoomPresenter,
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
notificationSettingsService.setRoomNotificationMode(room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||
val updatedState = consumeItemsUntilPredicate {
|
||||
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||
|
|
@ -458,11 +409,7 @@ class RoomDetailsPresenterTests {
|
|||
val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
|
||||
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
awaitItem().eventSink(RoomDetailsEvent.MuteNotification)
|
||||
val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) {
|
||||
it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE
|
||||
|
|
@ -480,11 +427,7 @@ class RoomDetailsPresenterTests {
|
|||
)
|
||||
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
|
||||
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
withFakeLifecycleOwner(fakeLifecycleOwner) {
|
||||
presenter.present()
|
||||
}
|
||||
}.test {
|
||||
presenter.test {
|
||||
awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification)
|
||||
val updatedState = consumeItemsUntilPredicate {
|
||||
it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES
|
||||
|
|
@ -493,6 +436,35 @@ class RoomDetailsPresenterTests {
|
|||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
|
||||
val setRoomIsFavoriteAction = FakeSetRoomIsFavoriteAction()
|
||||
val presenter = createRoomDetailsPresenter(setRoomIsFavoriteAction = setRoomIsFavoriteAction)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomDetailsEvent.SetIsFavorite(true))
|
||||
setRoomIsFavoriteAction.assertCalled(1)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - changes in notable tags updates is favorite flag`() = runTest {
|
||||
val room = aMatrixRoom()
|
||||
val presenter = createRoomDetailsPresenter(room = room)
|
||||
presenter.test {
|
||||
room.updateNotableTags(RoomNotableTags(true))
|
||||
consumeItemsUntilPredicate { it.isFavorite }.last().let { state ->
|
||||
assertThat(state.isFavorite).isTrue()
|
||||
}
|
||||
room.updateNotableTags(RoomNotableTags(false))
|
||||
consumeItemsUntilPredicate { !it.isFavorite }.last().let { state ->
|
||||
assertThat(state.isFavorite).isFalse()
|
||||
}
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun aMatrixRoom(
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ dependencies {
|
|||
implementation(projects.features.networkmonitor.api)
|
||||
implementation(projects.features.leaveroom.api)
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(projects.features.roomactions.api)
|
||||
api(projects.features.roomlist.api)
|
||||
ksp(libs.showkase.processor)
|
||||
|
||||
|
|
@ -75,4 +76,5 @@ dependencies {
|
|||
testImplementation(projects.features.networkmonitor.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(projects.features.leaveroom.test)
|
||||
testImplementation(projects.features.roomactions.test)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ fun RoomListContextMenu(
|
|||
eventSink(RoomListEvents.LeaveRoom(contextMenu.roomId))
|
||||
},
|
||||
onFavoriteChanged = { isFavorite ->
|
||||
eventSink(RoomListEvents.MarkRoomAsFavorite(contextMenu.roomId, isFavorite))
|
||||
eventSink(RoomListEvents.SetRoomIsFavorite(contextMenu.roomId, isFavorite))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,5 +28,5 @@ sealed interface RoomListEvents {
|
|||
data class ShowContextMenu(val roomListRoomSummary: RoomListRoomSummary) : RoomListEvents
|
||||
data object HideContextMenu : RoomListEvents
|
||||
data class LeaveRoom(val roomId: RoomId) : RoomListEvents
|
||||
data class MarkRoomAsFavorite(val roomId: RoomId, val isFavorite: Boolean) : RoomListEvents
|
||||
data class SetRoomIsFavorite(val roomId: RoomId, val isFavorite: Boolean) : RoomListEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
|||
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.roomactions.api.SetRoomIsFavoriteAction
|
||||
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
|
||||
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
|
|
@ -45,17 +46,14 @@ import io.element.android.libraries.indicator.api.IndicatorService
|
|||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val EXTENDED_RANGE_SIZE = 40
|
||||
|
|
@ -71,6 +69,7 @@ class RoomListPresenter @Inject constructor(
|
|||
private val encryptionService: EncryptionService,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val indicatorService: IndicatorService,
|
||||
private val setRoomIsFavorite: SetRoomIsFavoriteAction,
|
||||
) : Presenter<RoomListState> {
|
||||
@Composable
|
||||
override fun present(): RoomListState {
|
||||
|
|
@ -134,7 +133,7 @@ class RoomListPresenter @Inject constructor(
|
|||
contextMenu.value = RoomListState.ContextMenu.Hidden
|
||||
}
|
||||
is RoomListEvents.LeaveRoom -> leaveRoomState.eventSink(LeaveRoomEvent.ShowConfirmation(event.roomId))
|
||||
is RoomListEvents.MarkRoomAsFavorite -> coroutineScope.markRoomAsFavorite(event)
|
||||
is RoomListEvents.SetRoomIsFavorite -> coroutineScope.setRoomIsFavorite(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,28 +169,23 @@ class RoomListPresenter @Inject constructor(
|
|||
isFavorite = AsyncData.Loading(),
|
||||
)
|
||||
contextMenuState.value = initialState
|
||||
val room = client.getRoom(event.roomListRoomSummary.roomId)
|
||||
if (room != null) {
|
||||
room.notableTagsFlow
|
||||
.distinctUntilChanged()
|
||||
.onEach { tags ->
|
||||
val newState = initialState.copy(isFavorite = AsyncData.Success(tags.isFavorite))
|
||||
contextMenuState.value = newState
|
||||
}
|
||||
.launchIn(this)
|
||||
} else {
|
||||
contextMenuState.value = initialState.copy(isFavorite = AsyncData.Failure(IllegalStateException("Room not found")))
|
||||
client.getRoom(event.roomListRoomSummary.roomId).use { room ->
|
||||
if (room != null) {
|
||||
room.notableTagsFlow
|
||||
.distinctUntilChanged()
|
||||
.onEach { tags ->
|
||||
val newState = initialState.copy(isFavorite = AsyncData.Success(tags.isFavorite))
|
||||
contextMenuState.value = newState
|
||||
}
|
||||
.collect()
|
||||
} else {
|
||||
contextMenuState.value = initialState.copy(isFavorite = AsyncData.Failure(IllegalStateException("Room not found")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.markRoomAsFavorite(event: RoomListEvents.MarkRoomAsFavorite) = launch {
|
||||
val room = client.getRoom(event.roomId)
|
||||
if (room != null) {
|
||||
val notableTags = RoomNotableTags(isFavorite = event.isFavorite);
|
||||
room.updateNotableTags(notableTags)
|
||||
}else {
|
||||
Timber.w("Room ${event.roomId} not found, can't mark as favorite");
|
||||
}
|
||||
private fun CoroutineScope.setRoomIsFavorite(event: RoomListEvents.SetRoomIsFavorite) = launch {
|
||||
setRoomIsFavorite(event.roomId, event.isFavorite)
|
||||
}
|
||||
|
||||
private fun updateVisibleRange(range: IntRange) {
|
||||
|
|
@ -204,5 +198,3 @@ class RoomListPresenter @Inject constructor(
|
|||
client.roomListService.updateAllRoomsVisibleRange(extendedRange)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,11 +25,14 @@ import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
|||
import io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||
import io.element.android.features.roomactions.api.SetRoomIsFavoriteAction
|
||||
import io.element.android.features.roomactions.test.FakeSetRoomIsFavoriteAction
|
||||
import io.element.android.features.roomlist.impl.datasource.FakeInviteDataSource
|
||||
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
|
||||
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
||||
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
|
||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
|
||||
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
|
|
@ -44,6 +47,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
|||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
|
|
@ -55,6 +59,7 @@ import io.element.android.libraries.matrix.test.A_USER_NAME
|
|||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||
|
|
@ -309,19 +314,29 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - show context menu`() = runTest {
|
||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||
val presenter = createRoomListPresenter(coroutineScope = scope)
|
||||
val room = FakeMatrixRoom()
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
val presenter = createRoomListPresenter(client = client, coroutineScope = scope)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
|
||||
val initialState = awaitItem()
|
||||
val summary = aRoomListRoomSummary
|
||||
initialState.eventSink(RoomListEvents.ShowContextMenu(summary))
|
||||
|
||||
val shownState = awaitItem()
|
||||
assertThat(shownState.contextMenu)
|
||||
.isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name, false))
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contextMenu)
|
||||
.isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name, false, AsyncData.Success(false)))
|
||||
}
|
||||
|
||||
room.updateNotableTags(RoomNotableTags(isFavorite = true))
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contextMenu)
|
||||
.isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name, false, AsyncData.Success(true)))
|
||||
}
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
|
|
@ -329,7 +344,11 @@ class RoomListPresenterTests {
|
|||
@Test
|
||||
fun `present - hide context menu`() = runTest {
|
||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||
val presenter = createRoomListPresenter(coroutineScope = scope)
|
||||
val room = FakeMatrixRoom()
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
val presenter = createRoomListPresenter(client = client, coroutineScope = scope)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
|
|
@ -341,7 +360,7 @@ class RoomListPresenterTests {
|
|||
|
||||
val shownState = awaitItem()
|
||||
assertThat(shownState.contextMenu)
|
||||
.isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name, false))
|
||||
.isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name, false, AsyncData.Success(false)))
|
||||
shownState.eventSink(RoomListEvents.HideContextMenu)
|
||||
|
||||
val hiddenState = awaitItem()
|
||||
|
|
@ -394,6 +413,22 @@ class RoomListPresenterTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
|
||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||
val setRoomIsFavoriteAction = FakeSetRoomIsFavoriteAction()
|
||||
val presenter = createRoomListPresenter(setRoomIsFavoriteAction = setRoomIsFavoriteAction, coroutineScope = scope)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, true))
|
||||
setRoomIsFavoriteAction.assertCalled(1)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createRoomListPresenter(
|
||||
client: MatrixClient = FakeMatrixClient(),
|
||||
sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(),
|
||||
|
|
@ -407,6 +442,7 @@ class RoomListPresenterTests {
|
|||
roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(),
|
||||
encryptionService: EncryptionService = FakeEncryptionService(),
|
||||
coroutineScope: CoroutineScope,
|
||||
setRoomIsFavoriteAction: SetRoomIsFavoriteAction = FakeSetRoomIsFavoriteAction(),
|
||||
) = RoomListPresenter(
|
||||
client = client,
|
||||
sessionVerificationService = sessionVerificationService,
|
||||
|
|
@ -431,6 +467,7 @@ class RoomListPresenterTests {
|
|||
encryptionService = encryptionService,
|
||||
featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SecureStorage.key to true)),
|
||||
),
|
||||
setRoomIsFavorite = setRoomIsFavoriteAction,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ interface MatrixRoom : Closeable {
|
|||
|
||||
val roomInfoFlow: Flow<MatrixRoomInfo>
|
||||
|
||||
/**
|
||||
* The current notable tags as a Flow.
|
||||
*/
|
||||
val notableTagsFlow: Flow<RoomNotableTags>
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.room.tags
|
||||
|
||||
/**
|
||||
* Represents the notable tags of a room.
|
||||
* @param isFavorite true if the room is marked as favorite.
|
||||
*/
|
||||
data class RoomNotableTags(
|
||||
val isFavorite: Boolean,
|
||||
val isFavorite: Boolean = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ import org.matrix.rustcomponents.sdk.use
|
|||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import org.matrix.rustcomponents.sdk.Room as InnerRoom
|
||||
import uniffi.matrix_sdk_base.RoomNotableTags as RustRoomNotableTags
|
||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||
import uniffi.matrix_sdk_base.RoomNotableTags as RustRoomNotableTags
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RustMatrixRoom(
|
||||
|
|
@ -118,7 +118,6 @@ class RustMatrixRoom(
|
|||
override val notableTagsFlow: Flow<RoomNotableTags> = mxCallbackFlow {
|
||||
innerRoom.subscribeToNotableTags(object : RoomNotableTagsListener {
|
||||
override fun call(notableTags: RustRoomNotableTags) {
|
||||
Timber.d("On notable tags update: $notableTags")
|
||||
channel.trySend(notableTags.map())
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import org.matrix.rustcomponents.sdk.RoomInfo
|
|||
import org.matrix.rustcomponents.sdk.use
|
||||
|
||||
class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory()) {
|
||||
|
||||
fun create(roomInfo: RoomInfo): RoomSummaryDetails {
|
||||
val latestRoomMessage = roomInfo.latestEvent?.use {
|
||||
roomMessageFactory.create(it)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSende
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.Receipt
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import uniffi.matrix_sdk_ui.EventItemOrigin as RustEventItemOrigin
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.matrix.rustcomponents.sdk.Reaction
|
||||
|
|
@ -37,6 +36,7 @@ import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem
|
|||
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo
|
||||
import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails
|
||||
import org.matrix.rustcomponents.sdk.Receipt as RustReceipt
|
||||
import uniffi.matrix_sdk_ui.EventItemOrigin as RustEventItemOrigin
|
||||
|
||||
class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMapper = TimelineEventContentMapper()) {
|
||||
fun map(eventTimelineItem: RustEventTimelineItem): EventTimelineItem = eventTimelineItem.use {
|
||||
|
|
|
|||
|
|
@ -627,7 +627,6 @@ fun aRoomInfo(
|
|||
isPublic: Boolean = true,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: String? = null,
|
||||
alternativeAliases: List<String> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue