Send typing notification #2240

This commit is contained in:
Benoit Marty 2024-01-26 16:08:13 +01:00
parent 80270d1cc5
commit bfb6b32740
9 changed files with 62 additions and 2 deletions

View file

@ -43,6 +43,7 @@ sealed interface MessageComposerEvents {
data class ToggleTextFormatting(val enabled: Boolean) : MessageComposerEvents
data object CancelSendAttachment : MessageComposerEvents
data class Error(val error: Throwable) : MessageComposerEvents
data class TypingNotice(val isTyping: Boolean) : MessageComposerEvents
data class SuggestionReceived(val suggestion: Suggestion?) : MessageComposerEvents
data class InsertMention(val mention: MentionSuggestion) : MessageComposerEvents
}

View file

@ -20,6 +20,7 @@ import android.Manifest
import android.annotation.SuppressLint
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
@ -207,6 +208,15 @@ class MessageComposerPresenter @Inject constructor(
.collect()
}
DisposableEffect(Unit) {
// Declare that the user is not typing anymore when the composer is disposed
onDispose {
appCoroutineScope.launch {
room.typingNotice(false)
}
}
}
fun handleEvents(event: MessageComposerEvents) {
when (event) {
MessageComposerEvents.ToggleFullScreenState -> isFullScreen.value = !isFullScreen.value
@ -299,6 +309,11 @@ class MessageComposerPresenter @Inject constructor(
is MessageComposerEvents.Error -> {
analyticsService.trackError(event.error)
}
is MessageComposerEvents.TypingNotice -> {
localCoroutineScope.launch {
room.typingNotice(event.isTyping)
}
}
is MessageComposerEvents.SuggestionReceived -> {
suggestionSearchTrigger.value = event.suggestion
}

View file

@ -78,6 +78,10 @@ internal fun MessageComposerView(
state.eventSink(MessageComposerEvents.Error(error))
}
fun onTyping(typing: Boolean) {
state.eventSink(MessageComposerEvents.TypingNotice(typing))
}
val coroutineScope = rememberCoroutineScope()
fun onRequestFocus() {
coroutineScope.launch {
@ -121,6 +125,7 @@ internal fun MessageComposerView(
onDeleteVoiceMessage = onDeleteVoiceMessage,
onSuggestionReceived = ::onSuggestionReceived,
onError = ::onError,
onTyping = ::onTyping,
currentUserId = state.currentUserId,
onRichContentSelected = ::sendUri,
)

View file

@ -873,6 +873,21 @@ class MessageComposerPresenterTest {
}
}
@Test
fun `present - handle typing notice event`() = runTest {
val room = FakeMatrixRoom()
val presenter = createPresenter(room = room, coroutineScope = this)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitFirstItem()
assertThat(room.typingRecord).isEmpty()
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
assertThat(room.typingRecord).isEqualTo(listOf(true, false))
}
}
private suspend fun ReceiveTurbine<MessageComposerState>.backToNormalMode(state: MessageComposerState, skipCount: Int = 0): MessageComposerState {
state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode)
skipItems(skipCount)