diff --git a/build.gradle.kts b/build.gradle.kts index 051a7a4112..26b7a4efe5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -60,7 +60,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.3.9") + detektPlugins("io.nlopez.compose.rules:detekt:0.3.10") } // KtLint diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt index d2674e3f98..54a1b541be 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenView.kt @@ -18,6 +18,8 @@ package io.element.android.features.ftue.impl.migration import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import io.element.android.features.ftue.impl.R @@ -32,8 +34,9 @@ fun MigrationScreenView( modifier: Modifier = Modifier, ) { if (migrationState.isMigrating.not()) { + val latestOnMigrationFinished by rememberUpdatedState(onMigrationFinished) LaunchedEffect(Unit) { - onMigrationFinished() + latestOnMigrationFinished() } } SunsetPage( diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt index 28ecf4938d..ff8ba55c0b 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt @@ -28,6 +28,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -57,8 +59,9 @@ fun InviteListView( modifier: Modifier = Modifier, ) { if (state.acceptedAction is AsyncData.Success) { + val latestOnInviteAccepted by rememberUpdatedState(onInviteAccepted) LaunchedEffect(state.acceptedAction) { - onInviteAccepted(state.acceptedAction.data) + latestOnInviteAccepted(state.acceptedAction.data) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockHelper.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockHelper.kt index e1dd7f5be3..6ecf13f591 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockHelper.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockHelper.kt @@ -18,6 +18,8 @@ package io.element.android.features.lockscreen.impl.unlock import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockManager import io.element.android.features.lockscreen.impl.biometric.DefaultBiometricUnlockCallback import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback @@ -30,15 +32,16 @@ class PinUnlockHelper @Inject constructor( ) { @Composable fun OnUnlockEffect(onUnlock: () -> Unit) { + val latestOnUnlock by rememberUpdatedState(onUnlock) DisposableEffect(Unit) { val biometricUnlockCallback = object : DefaultBiometricUnlockCallback() { override fun onBiometricUnlockSuccess() { - onUnlock() + latestOnUnlock() } } val pinCodeVerifiedCallback = object : DefaultPinCodeManagerCallback() { override fun onPinCodeVerified() { - onUnlock() + latestOnUnlock() } } biometricUnlockManager.addCallback(biometricUnlockCallback) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index 132003eafc..9f71ccc7e5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -18,6 +18,8 @@ package io.element.android.features.login.impl.changeserver import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog @@ -63,8 +65,11 @@ fun ChangeServerView( } } is AsyncData.Loading -> ProgressDialog() - is AsyncData.Success -> LaunchedEffect(state.changeServerAction) { - onDone() + is AsyncData.Success -> { + val latestOnDone by rememberUpdatedState(onDone) + LaunchedEffect(state.changeServerAction) { + latestOnDone() + } } AsyncData.Uninitialized -> Unit } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt index 636dd68058..bf103745b4 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt @@ -18,6 +18,8 @@ package io.element.android.features.logout.impl.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.res.stringResource import io.element.android.features.logout.impl.R import io.element.android.libraries.architecture.AsyncAction @@ -52,9 +54,11 @@ fun LogoutActionDialog( onRetry = onForceLogoutClicked, onDismiss = onDismissError, ) - is AsyncAction.Success -> + is AsyncAction.Success -> { + val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout) LaunchedEffect(state) { - onSuccessLogout(state.data) + latestOnSuccessLogout(state.data) } + } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 394f1f3556..be3afb6660 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -41,8 +41,10 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -294,8 +296,11 @@ private fun AttachmentStateView( ) { when (state) { AttachmentsState.None -> Unit - is AttachmentsState.Previewing -> LaunchedEffect(state) { - onPreviewAttachments(state.attachments) + is AttachmentsState.Previewing -> { + val latestOnPreviewAttachments by rememberUpdatedState(onPreviewAttachments) + LaunchedEffect(state) { + latestOnPreviewAttachments(state.attachments) + } } is AttachmentsState.Sending -> { ProgressDialog( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 5b624bd254..b18d36d61d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -24,6 +24,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -57,8 +59,9 @@ fun AttachmentsPreviewView( } if (state.sendActionState is SendActionState.Done) { + val latestOnDismiss by rememberUpdatedState(onDismiss) LaunchedEffect(state.sendActionState) { - onDismiss() + latestOnDismiss() } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index d2ab635e6a..563eb78d78 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -42,6 +42,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -193,10 +194,11 @@ private fun BoxScope.TimelineScrollHelper( } } + val latestOnScrollFinishedAt by rememberUpdatedState(onScrollFinishedAt) LaunchedEffect(isScrollFinished, isTimelineEmpty) { if (isScrollFinished && !isTimelineEmpty) { // Notify the parent composable about the first visible item index when scrolling finishes - onScrollFinishedAt(lazyListState.firstVisibleItemIndex) + latestOnScrollFinishedAt(lazyListState.firstVisibleItemIndex) } } diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt index 996392d879..e076ceb1a6 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt @@ -18,6 +18,8 @@ package io.element.android.features.rageshake.api.detection import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource @@ -73,9 +75,10 @@ private fun TakeScreenshot( onScreenshotTaken: (ImageResult) -> Unit ) { val view = LocalView.current + val latestOnScreenshotTaken by rememberUpdatedState(onScreenshotTaken) LaunchedEffect(Unit) { view.screenshot { - onScreenshotTaken(it) + latestOnScreenshotTaken(it) } } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenter.kt index c12ea2d24e..73d8058036 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenter.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import io.element.android.features.securebackup.impl.loggerTagDisable -import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -36,14 +36,14 @@ class SecureBackupEnablePresenter @Inject constructor( ) : Presenter { @Composable override fun present(): SecureBackupEnableState { - val enableAction = remember { mutableStateOf>(AsyncData.Uninitialized) } + val enableAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val coroutineScope = rememberCoroutineScope() fun handleEvents(event: SecureBackupEnableEvents) { when (event) { is SecureBackupEnableEvents.EnableBackup -> coroutineScope.enableBackup(enableAction) SecureBackupEnableEvents.DismissDialog -> { - enableAction.value = AsyncData.Uninitialized + enableAction.value = AsyncAction.Uninitialized } } } @@ -54,7 +54,7 @@ class SecureBackupEnablePresenter @Inject constructor( ) } - private fun CoroutineScope.enableBackup(action: MutableState>) = launch { + private fun CoroutineScope.enableBackup(action: MutableState>) = launch { suspend { Timber.tag(loggerTagDisable.value).d("Calling encryptionService.enableBackups()") encryptionService.enableBackups().getOrThrow() diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableState.kt index bd81004f9c..7c38501bf0 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableState.kt @@ -16,9 +16,9 @@ package io.element.android.features.securebackup.impl.enable -import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.AsyncAction data class SecureBackupEnableState( - val enableAction: AsyncData, + val enableAction: AsyncAction, val eventSink: (SecureBackupEnableEvents) -> Unit ) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableStateProvider.kt index 1029278c05..fc48036626 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableStateProvider.kt @@ -17,20 +17,20 @@ package io.element.android.features.securebackup.impl.enable import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.AsyncAction open class SecureBackupEnableStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSecureBackupEnableState(), - aSecureBackupEnableState(enableAction = AsyncData.Loading()), - aSecureBackupEnableState(enableAction = AsyncData.Failure(Exception("Failed to enable"))), + aSecureBackupEnableState(enableAction = AsyncAction.Loading), + aSecureBackupEnableState(enableAction = AsyncAction.Failure(Exception("Failed to enable"))), // Add other states here ) } fun aSecureBackupEnableState( - enableAction: AsyncData = AsyncData.Uninitialized, + enableAction: AsyncAction = AsyncAction.Uninitialized, ) = SecureBackupEnableState( enableAction = enableAction, eventSink = {} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt index 37a46a1408..db41289ecc 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt @@ -19,16 +19,14 @@ package io.element.android.features.securebackup.impl.enable import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.securebackup.impl.R -import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage -import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog +import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button @@ -41,11 +39,6 @@ fun SecureBackupEnableView( onBackClicked: () -> Unit, modifier: Modifier = Modifier, ) { - LaunchedEffect(state.enableAction) { - if (state.enableAction is AsyncData.Success) { - onDone() - } - } FlowStepPage( modifier = modifier, onBackClicked = onBackClicked, @@ -53,12 +46,12 @@ fun SecureBackupEnableView( iconVector = ImageVector.vectorResource(CommonDrawables.ic_key), buttons = { Buttons(state = state) } ) - if (state.enableAction is AsyncData.Failure) { - ErrorDialog( - content = state.enableAction.error.let { it.message ?: it.toString() }, - onDismiss = { state.eventSink.invoke(SecureBackupEnableEvents.DismissDialog) }, - ) - } + AsyncActionView( + async = state.enableAction, + progressDialog = { }, + onSuccess = { onDone() }, + onErrorDismiss = { state.eventSink.invoke(SecureBackupEnableEvents.DismissDialog) } + ) } @Composable diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenterTest.kt index 39d8ebf81f..4ee16285cd 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnablePresenterTest.kt @@ -20,7 +20,7 @@ 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.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService @@ -40,7 +40,7 @@ class SecureBackupEnablePresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.enableAction).isEqualTo(AsyncData.Uninitialized) + assertThat(initialState.enableAction).isEqualTo(AsyncAction.Uninitialized) } } @@ -53,9 +53,9 @@ class SecureBackupEnablePresenterTest { val initialState = awaitItem() initialState.eventSink(SecureBackupEnableEvents.EnableBackup) val loadingState = awaitItem() - assertThat(loadingState.enableAction).isInstanceOf(AsyncData.Loading::class.java) + assertThat(loadingState.enableAction).isInstanceOf(AsyncAction.Loading::class.java) val finalState = awaitItem() - assertThat(finalState.enableAction).isEqualTo(AsyncData.Success(Unit)) + assertThat(finalState.enableAction).isEqualTo(AsyncAction.Success(Unit)) } } @@ -70,12 +70,12 @@ class SecureBackupEnablePresenterTest { val initialState = awaitItem() initialState.eventSink(SecureBackupEnableEvents.EnableBackup) val loadingState = awaitItem() - assertThat(loadingState.enableAction).isInstanceOf(AsyncData.Loading::class.java) + assertThat(loadingState.enableAction).isInstanceOf(AsyncAction.Loading::class.java) val errorState = awaitItem() - assertThat(errorState.enableAction).isEqualTo(AsyncData.Failure(AN_EXCEPTION)) + assertThat(errorState.enableAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION)) errorState.eventSink(SecureBackupEnableEvents.DismissDialog) val finalState = awaitItem() - assertThat(finalState.enableAction).isEqualTo(AsyncData.Uninitialized) + assertThat(finalState.enableAction).isEqualTo(AsyncAction.Uninitialized) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt index 732a3473a3..f82553d731 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncActionView.kt @@ -18,6 +18,8 @@ package io.element.android.libraries.designsystem.components.async import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.ProgressDialog @@ -67,8 +69,9 @@ fun AsyncActionView( } } is AsyncAction.Success -> { + val latestOnSuccess by rememberUpdatedState(onSuccess) LaunchedEffect(async) { - onSuccess(async.data) + latestOnSuccess(async.data) } } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/SoftKeyboardEffect.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/SoftKeyboardEffect.kt index 96b48dca6e..fd5f043b32 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/SoftKeyboardEffect.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/SoftKeyboardEffect.kt @@ -18,6 +18,8 @@ package io.element.android.libraries.textcomposer import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.platform.LocalView import androidx.compose.ui.viewinterop.AndroidView import io.element.android.libraries.androidutils.ui.awaitWindowFocus @@ -40,8 +42,10 @@ internal fun SoftKeyboardEffect( predicate: (T) -> Boolean, ) { val view = LocalView.current + val latestOnRequestFocus by rememberUpdatedState(onRequestFocus) + val latestPredicate by rememberUpdatedState(predicate) LaunchedEffect(key) { - if (predicate(key)) { + if (latestPredicate(key)) { // Await window focus in case returning from a dialog view.awaitWindowFocus() @@ -49,7 +53,7 @@ internal fun SoftKeyboardEffect( view.showKeyboard(andRequestFocus = true) // Refocus to the correct view - onRequestFocus() + latestOnRequestFocus() } } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 06585d3b31..252e35d652 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -42,6 +42,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -274,12 +275,13 @@ fun TextComposer( } val menuAction = state.menuAction + val latestOnSuggestionReceived by rememberUpdatedState(onSuggestionReceived) LaunchedEffect(menuAction) { if (menuAction is MenuAction.Suggestion) { val suggestion = Suggestion(menuAction.suggestionPattern) - onSuggestionReceived(suggestion) + latestOnSuggestionReceived(suggestion) } else { - onSuggestionReceived(null) + latestOnSuggestionReceived(null) } } }