Typing notification: disable rendering when the user preference isRenderTypingNotificationsEnabled is false.
This commit is contained in:
parent
d27df29452
commit
5213849f8b
6 changed files with 80 additions and 18 deletions
|
|
@ -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
|
|||
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
|
|||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ class TypingNotificationStateProvider : PreviewParameterProvider<TypingNotificat
|
|||
internal fun aTypingNotificationState(
|
||||
typingMembers: List<RoomMember> = emptyList(),
|
||||
) = TypingNotificationState(
|
||||
renderTypingNotifications = true,
|
||||
typingMembers = typingMembers.toImmutableList(),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
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 {
|
|||
matrixRoom: MatrixRoom = FakeMatrixRoom().apply {
|
||||
givenRoomInfo(aRoomInfo(id = roomId.value, name = ""))
|
||||
},
|
||||
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(
|
||||
isRenderTypingNotificationsEnabled = true
|
||||
),
|
||||
): TypingNotificationPresenter {
|
||||
return TypingNotificationPresenter(
|
||||
room = matrixRoom,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue