Browse Source

Typing notification: disable rendering when the user preference `isRenderTypingNotificationsEnabled` is false.

pull/2374/head
Benoit Marty 7 months ago
parent
commit
5213849f8b
  1. 46
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt
  2. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt
  3. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt
  4. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt
  5. 5
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt
  6. 43
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt

46
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenter.kt

@ -18,10 +18,12 @@ package io.element.android.features.messages.impl.typing @@ -18,10 +18,12 @@ package io.element.android.features.messages.impl.typing
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom
@ -29,6 +31,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember @@ -29,6 +31,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.room.roomMembers
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
@ -37,30 +40,41 @@ import javax.inject.Inject @@ -37,30 +40,41 @@ import javax.inject.Inject
class TypingNotificationPresenter @Inject constructor(
private val room: MatrixRoom,
private val sessionPreferencesStore: SessionPreferencesStore,
) : Presenter<TypingNotificationState> {
@Composable
override fun present(): TypingNotificationState {
var typingMembers by remember { mutableStateOf(emptyList<RoomMember>()) }
LaunchedEffect(Unit) {
combine(room.roomTypingMembersFlow, room.membersStateFlow) { typingMembers, membersState ->
typingMembers
.map { userId ->
membersState.roomMembers()
?.firstOrNull { roomMember -> roomMember.userId == userId }
?: createDefaultRoomMemberForTyping(userId)
}
val typingMembersState = remember { mutableStateOf(emptyList<RoomMember>()) }
val renderTypingNotifications by sessionPreferencesStore.isRenderTypingNotificationsEnabled().collectAsState(initial = true)
LaunchedEffect(renderTypingNotifications) {
if (renderTypingNotifications) {
observeRoomTypingMembers(typingMembersState)
} else {
typingMembersState.value = emptyList()
}
.distinctUntilChanged()
.onEach { members ->
typingMembers = members
}
.launchIn(this)
}
return TypingNotificationState(
typingMembers = typingMembers.toImmutableList(),
renderTypingNotifications = renderTypingNotifications,
typingMembers = typingMembersState.value.toImmutableList(),
)
}
private fun CoroutineScope.observeRoomTypingMembers(typingMembersState: MutableState<List<RoomMember>>) {
combine(room.roomTypingMembersFlow, room.membersStateFlow) { typingMembers, membersState ->
typingMembers
.map { userId ->
membersState.roomMembers()
?.firstOrNull { roomMember -> roomMember.userId == userId }
?: createDefaultRoomMemberForTyping(userId)
}
}
.distinctUntilChanged()
.onEach { members ->
typingMembersState.value = members
}
.launchIn(this)
}
}
/**

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationState.kt

@ -20,5 +20,6 @@ import io.element.android.libraries.matrix.api.room.RoomMember @@ -20,5 +20,6 @@ import io.element.android.libraries.matrix.api.room.RoomMember
import kotlinx.collections.immutable.ImmutableList
data class TypingNotificationState(
val renderTypingNotifications: Boolean,
val typingMembers: ImmutableList<RoomMember>,
)

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt

@ -74,6 +74,7 @@ class TypingNotificationStateProvider : PreviewParameterProvider<TypingNotificat @@ -74,6 +74,7 @@ class TypingNotificationStateProvider : PreviewParameterProvider<TypingNotificat
internal fun aTypingNotificationState(
typingMembers: List<RoomMember> = emptyList(),
) = TypingNotificationState(
renderTypingNotifications = true,
typingMembers = typingMembers.toImmutableList(),
)

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationView.kt

@ -42,7 +42,7 @@ fun TypingNotificationView( @@ -42,7 +42,7 @@ fun TypingNotificationView(
state: TypingNotificationState,
modifier: Modifier = Modifier,
) {
if (state.typingMembers.isEmpty()) return
if (state.typingMembers.isEmpty() || !state.renderTypingNotifications) return
val typingNotificationText = computeTypingNotificationText(state.typingMembers)
Text(
modifier = modifier

5
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt

@ -731,7 +731,10 @@ class MessagesPresenterTest { @@ -731,7 +731,10 @@ class MessagesPresenterTest {
}
}
val actionListPresenter = ActionListPresenter(appPreferencesStore = appPreferencesStore)
val typingNotificationPresenter = TypingNotificationPresenter(matrixRoom)
val typingNotificationPresenter = TypingNotificationPresenter(
room = matrixRoom,
sessionPreferencesStore = sessionPreferencesStore,
)
val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter()
val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)

43
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationPresenterTest.kt

@ -20,6 +20,8 @@ import app.cash.molecule.RecompositionMode @@ -20,6 +20,8 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesStore
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
@ -49,10 +51,47 @@ class TypingNotificationPresenterTest { @@ -49,10 +51,47 @@ class TypingNotificationPresenterTest {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.renderTypingNotifications).isTrue()
assertThat(initialState.typingMembers).isEmpty()
}
}
@Test
fun `present - typing notification disabled`() = runTest {
val aDefaultRoomMember = createDefaultRoomMember(A_USER_ID_2)
val room = FakeMatrixRoom()
val sessionPreferencesStore = InMemorySessionPreferencesStore(
isRenderTypingNotificationsEnabled = false
)
val presenter = createPresenter(
matrixRoom = room,
sessionPreferencesStore = sessionPreferencesStore,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.renderTypingNotifications).isFalse()
assertThat(initialState.typingMembers).isEmpty()
room.givenRoomTypingMembers(listOf(A_USER_ID_2))
expectNoEvents()
// Preferences changes
sessionPreferencesStore.setRenderTypingNotifications(true)
skipItems(1)
val oneMemberTypingState = awaitItem()
assertThat(oneMemberTypingState.renderTypingNotifications).isTrue()
assertThat(oneMemberTypingState.typingMembers.size).isEqualTo(1)
assertThat(oneMemberTypingState.typingMembers.first()).isEqualTo(aDefaultRoomMember)
// Preferences changes again
sessionPreferencesStore.setRenderTypingNotifications(false)
skipItems(1)
val finalState = awaitItem()
assertThat(finalState.renderTypingNotifications).isFalse()
assertThat(finalState.typingMembers).isEmpty()
}
}
@Test
fun `present - state is updated when a member is typing, member is not known`() = runTest {
val aDefaultRoomMember = createDefaultRoomMember(A_USER_ID_2)
@ -136,9 +175,13 @@ class TypingNotificationPresenterTest { @@ -136,9 +175,13 @@ class TypingNotificationPresenterTest {
matrixRoom: MatrixRoom = FakeMatrixRoom().apply {
givenRoomInfo(aRoomInfo(id = roomId.value, name = ""))
},
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(
isRenderTypingNotificationsEnabled = true
),
): TypingNotificationPresenter {
return TypingNotificationPresenter(
room = matrixRoom,
sessionPreferencesStore = sessionPreferencesStore,
)
}

Loading…
Cancel
Save