Fix crash when starting a DM (#6419)
`AnchoredDraggable.requireOffset` was called before it was populated when displaying `CreateDmConfirmationBottomSheet`, because the keyboard and the bottom sheet were causing conflicting animations related to the insets. Hiding the keyboard before displaying the bottom sheet seems to fix the issue, and `skipPartiallyExpanded` results in a better UX (and also worked around the issue by itself).
This commit is contained in:
committed by
GitHub
parent
93ab9f43dc
commit
9074692189
@@ -21,8 +21,10 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -31,6 +33,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons
|
|||||||
import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser
|
import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser
|
||||||
import io.element.android.features.startchat.impl.R
|
import io.element.android.features.startchat.impl.R
|
||||||
import io.element.android.features.startchat.impl.components.UserListView
|
import io.element.android.features.startchat.impl.components.UserListView
|
||||||
|
import io.element.android.libraries.androidutils.ui.hideKeyboardAndAwaitAnimation
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
|
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
@@ -47,6 +50,7 @@ import io.element.android.libraries.matrix.ui.components.CreateDmConfirmationBot
|
|||||||
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
|
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StartChatView(
|
fun StartChatView(
|
||||||
@@ -59,6 +63,8 @@ fun StartChatView(
|
|||||||
onRoomDirectorySearchClick: () -> Unit,
|
onRoomDirectorySearchClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -73,6 +79,8 @@ fun StartChatView(
|
|||||||
.consumeWindowInsets(paddingValues),
|
.consumeWindowInsets(paddingValues),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
|
val view = LocalView.current
|
||||||
|
|
||||||
UserListView(
|
UserListView(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
// Do not render suggestions in this case, the suggestion will be rendered
|
// Do not render suggestions in this case, the suggestion will be rendered
|
||||||
@@ -81,7 +89,10 @@ fun StartChatView(
|
|||||||
recentDirectRooms = persistentListOf(),
|
recentDirectRooms = persistentListOf(),
|
||||||
),
|
),
|
||||||
onSelectUser = {
|
onSelectUser = {
|
||||||
state.eventSink(StartChatEvents.StartDM(it))
|
coroutineScope.launch {
|
||||||
|
view.hideKeyboardAndAwaitAnimation()
|
||||||
|
state.eventSink(StartChatEvents.StartDM(it))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onDeselectUser = { },
|
onDeselectUser = { },
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,12 +9,15 @@
|
|||||||
package io.element.android.libraries.androidutils.ui
|
package io.element.android.libraries.androidutils.ui
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.ResultReceiver
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.view.WindowInsets
|
import android.view.WindowInsets
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
fun View.hideKeyboard() {
|
fun View.hideKeyboard() {
|
||||||
@@ -22,6 +25,32 @@ fun View.hideKeyboard() {
|
|||||||
imm?.hideSoftInputFromWindow(windowToken, 0)
|
imm?.hideSoftInputFromWindow(windowToken, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun View.hideKeyboardAndAwaitAnimation() {
|
||||||
|
val imm = context?.getSystemService<InputMethodManager>()
|
||||||
|
|
||||||
|
val mutex = Mutex()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
setOnApplyWindowInsetsListener { view, insets ->
|
||||||
|
if (!insets.isVisible(WindowInsets.Type.ime())) {
|
||||||
|
mutex.unlock()
|
||||||
|
}
|
||||||
|
insets
|
||||||
|
}
|
||||||
|
imm?.hideSoftInputFromWindow(windowToken, 0)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
imm?.hideSoftInputFromWindow(windowToken, 0, object : ResultReceiver(null) {
|
||||||
|
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
|
||||||
|
if (resultCode == InputMethodManager.RESULT_UNCHANGED_HIDDEN ||
|
||||||
|
resultCode == InputMethodManager.RESULT_HIDDEN) {
|
||||||
|
mutex.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
mutex.lock()
|
||||||
|
}
|
||||||
|
|
||||||
fun View.showKeyboard(andRequestFocus: Boolean = false) {
|
fun View.showKeyboard(andRequestFocus: Boolean = false) {
|
||||||
if (andRequestFocus) {
|
if (andRequestFocus) {
|
||||||
requestFocus()
|
requestFocus()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -54,14 +55,14 @@ fun CreateDmConfirmationBottomSheet(
|
|||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
|
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp),
|
.padding(top = 24.dp, bottom = 16.dp, start = 16.dp, end = 16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
Avatar(
|
Avatar(
|
||||||
avatarData = matrixUser.getAvatarData(AvatarSize.DmCreationConfirmation),
|
avatarData = matrixUser.getAvatarData(AvatarSize.DmCreationConfirmation),
|
||||||
avatarType = AvatarType.User,
|
avatarType = AvatarType.User,
|
||||||
@@ -93,7 +94,6 @@ fun CreateDmConfirmationBottomSheet(
|
|||||||
onClick = onDismiss,
|
onClick = onDismiss,
|
||||||
text = stringResource(CommonStrings.action_cancel),
|
text = stringResource(CommonStrings.action_cancel),
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user