When joining a call, wait for the content_loaded action (#5399)

* When joining a call, wait for the `content_loaded` action

This ensures the widget won't be disposed when the user joins a call and loads the lobby screen for room calls
This commit is contained in:
Jorge Martin Espinosa
2025-09-23 17:18:53 +02:00
committed by GitHub
parent 0a5c178fe8
commit cc38651c44
3 changed files with 13 additions and 10 deletions
@@ -41,5 +41,8 @@ data class WidgetMessage(
@SerialName("send_event") @SerialName("send_event")
SendEvent, SendEvent,
@SerialName("content_loaded")
ContentLoaded,
} }
} }
@@ -79,7 +79,7 @@ class CallScreenPresenter(
val urlState = remember { mutableStateOf<AsyncData<String>>(AsyncData.Uninitialized) } val urlState = remember { mutableStateOf<AsyncData<String>>(AsyncData.Uninitialized) }
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) } val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) } val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
var isJoinedCall by rememberSaveable { mutableStateOf(false) } var isWidgetLoaded by rememberSaveable { mutableStateOf(false) }
var ignoreWebViewError by rememberSaveable { mutableStateOf(false) } var ignoreWebViewError by rememberSaveable { mutableStateOf(false) }
var webViewError by remember { mutableStateOf<String?>(null) } var webViewError by remember { mutableStateOf<String?>(null) }
val languageTag = languageTagProvider.provideLanguageTag() val languageTag = languageTagProvider.provideLanguageTag()
@@ -139,8 +139,8 @@ class CallScreenPresenter(
if (parsedMessage?.direction == WidgetMessage.Direction.FromWidget) { if (parsedMessage?.direction == WidgetMessage.Direction.FromWidget) {
if (parsedMessage.action == WidgetMessage.Action.Close) { if (parsedMessage.action == WidgetMessage.Action.Close) {
close(callWidgetDriver.value, navigator) close(callWidgetDriver.value, navigator)
} else if (parsedMessage.action == WidgetMessage.Action.Join) { } else if (parsedMessage.action == WidgetMessage.Action.ContentLoaded) {
isJoinedCall = true isWidgetLoaded = true
} }
} }
} }
@@ -151,8 +151,8 @@ class CallScreenPresenter(
// Wait for the call to be joined, if it takes too long, we display an error // Wait for the call to be joined, if it takes too long, we display an error
delay(10.seconds) delay(10.seconds)
if (!isJoinedCall) { if (!isWidgetLoaded) {
Timber.w("The call took too long to be joined. Displaying an error before exiting.") Timber.w("The call took too long to load. Displaying an error before exiting.")
// This will display a simple 'Sorry, an error occurred' dialog and force the user to exit the call // This will display a simple 'Sorry, an error occurred' dialog and force the user to exit the call
webViewError = "" webViewError = ""
@@ -165,10 +165,10 @@ class CallScreenPresenter(
is CallScreenEvents.Hangup -> { is CallScreenEvents.Hangup -> {
val widgetId = callWidgetDriver.value?.id val widgetId = callWidgetDriver.value?.id
val interceptor = messageInterceptor.value val interceptor = messageInterceptor.value
if (widgetId != null && interceptor != null && isJoinedCall) { if (widgetId != null && interceptor != null && isWidgetLoaded) {
// If the call was joined, we need to hang up first. Then the UI will be dismissed automatically. // If the call was joined, we need to hang up first. Then the UI will be dismissed automatically.
sendHangupMessage(widgetId, interceptor) sendHangupMessage(widgetId, interceptor)
isJoinedCall = false isWidgetLoaded = false
coroutineScope.launch { coroutineScope.launch {
// Wait for a couple of seconds to receive the hangup message // Wait for a couple of seconds to receive the hangup message
@@ -198,7 +198,7 @@ class CallScreenPresenter(
urlState = urlState.value, urlState = urlState.value,
webViewError = webViewError, webViewError = webViewError,
userAgent = userAgent, userAgent = userAgent,
isCallActive = isJoinedCall, isCallActive = isWidgetLoaded,
isInWidgetMode = isInWidgetMode, isInWidgetMode = isInWidgetMode,
eventSink = { handleEvents(it) }, eventSink = { handleEvents(it) },
) )
@@ -215,7 +215,7 @@ import kotlin.time.Duration.Companion.seconds
} }
@Test @Test
fun `present - a received 'joined' action makes the call to be active`() = runTest { fun `present - a received 'content loaded' action makes the call to be active`() = runTest {
val navigator = FakeCallScreenNavigator() val navigator = FakeCallScreenNavigator()
val widgetDriver = FakeMatrixWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
@@ -238,7 +238,7 @@ import kotlin.time.Duration.Companion.seconds
messageInterceptor.givenInterceptedMessage( messageInterceptor.givenInterceptedMessage(
""" """
{ {
"action":"io.element.join", "action":"content_loaded",
"api":"fromWidget", "api":"fromWidget",
"widgetId":"1", "widgetId":"1",
"requestId":"1" "requestId":"1"