Merge pull request #3049 from element-hq/feature/bma/hiddenText
Fix recovery key field hidden when keyboard is opened.
This commit is contained in:
@@ -17,16 +17,22 @@
|
||||
package io.element.android.features.login.impl.changeserver
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.login.impl.error.ChangeServerError
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerState> {
|
||||
override val values: Sequence<ChangeServerState>
|
||||
get() = sequenceOf(
|
||||
aChangeServerState(),
|
||||
aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.Error(CommonStrings.error_unknown))),
|
||||
aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.SlidingSyncAlert)),
|
||||
)
|
||||
}
|
||||
|
||||
fun aChangeServerState() = ChangeServerState(
|
||||
changeServerAction = AsyncData.Uninitialized,
|
||||
fun aChangeServerState(
|
||||
changeServerAction: AsyncData<Unit> = AsyncData.Uninitialized,
|
||||
) = ChangeServerState(
|
||||
changeServerAction = changeServerAction,
|
||||
eventSink = {}
|
||||
)
|
||||
|
||||
@@ -55,6 +55,7 @@ fun SecureBackupEnterRecoveryKeyView(
|
||||
|
||||
FlowStepPage(
|
||||
modifier = modifier,
|
||||
isScrollable = true,
|
||||
onBackClick = onBackClick,
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.KeySolid()),
|
||||
title = stringResource(id = R.string.screen_recovery_key_confirm_title),
|
||||
@@ -70,7 +71,7 @@ private fun Content(
|
||||
state: SecureBackupEnterRecoveryKeyState,
|
||||
) {
|
||||
RecoveryKeyView(
|
||||
modifier = Modifier.padding(top = 52.dp),
|
||||
modifier = Modifier.padding(top = 52.dp, bottom = 32.dp),
|
||||
state = state.recoveryKeyViewState,
|
||||
onClick = null,
|
||||
onChange = {
|
||||
|
||||
@@ -19,8 +19,12 @@ package io.element.android.features.securebackup.impl.enter
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performImeAction
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.securebackup.impl.R
|
||||
import io.element.android.features.securebackup.impl.setup.views.aFormattedRecoveryKey
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
@@ -33,6 +37,7 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SecureBackupEnterRecoveryKeyViewTest {
|
||||
@@ -61,6 +66,7 @@ class SecureBackupEnterRecoveryKeyViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `tapping on Continue when key is valid - calls expected action`() {
|
||||
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>()
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
@@ -72,6 +78,31 @@ class SecureBackupEnterRecoveryKeyViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `entering a char emits the expected event`() {
|
||||
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>()
|
||||
val keyValue = aFormattedRecoveryKey()
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder),
|
||||
)
|
||||
rule.onNodeWithText(keyValue).performTextInput("X")
|
||||
recorder.assertSingle(
|
||||
SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange("X$keyValue")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validating from keyboard emits the expected event`() {
|
||||
val recorder = EventsRecorder<SecureBackupEnterRecoveryKeyEvents>()
|
||||
val keyValue = aFormattedRecoveryKey()
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder),
|
||||
)
|
||||
rule.onNodeWithText(keyValue).performImeAction()
|
||||
recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `tapping on Lost your recovery key - calls onCreateNewRecoveryKey`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setSecureBackupEnterRecoveryKeyView(
|
||||
@@ -98,7 +129,7 @@ class SecureBackupEnterRecoveryKeyViewTest {
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
onCreateNewRecoveryKey: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
rule.setContent {
|
||||
setContent {
|
||||
SecureBackupEnterRecoveryKeyView(
|
||||
state = state,
|
||||
onSuccess = onDone,
|
||||
|
||||
@@ -218,7 +218,7 @@ class VerifySelfSessionViewTest {
|
||||
onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(),
|
||||
onFinished: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
rule.setContent {
|
||||
setContent {
|
||||
VerifySelfSessionView(
|
||||
state = state,
|
||||
onEnterRecoveryKey = onEnterRecoveryKey,
|
||||
|
||||
@@ -52,6 +52,7 @@ fun FlowStepPage(
|
||||
iconStyle: BigIcon.Style,
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
isScrollable: Boolean = false,
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
subTitle: String? = null,
|
||||
buttons: @Composable ColumnScope.() -> Unit = {},
|
||||
@@ -62,6 +63,7 @@ fun FlowStepPage(
|
||||
}
|
||||
HeaderFooterPage(
|
||||
modifier = modifier,
|
||||
isScrollable = isScrollable,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
|
||||
@@ -22,7 +22,9 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -39,6 +41,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
||||
* @param modifier Classical modifier.
|
||||
* @param paddingValues padding values to apply to the content.
|
||||
* @param containerColor color of the container. Set to [Color.Transparent] if you provide a background in the [modifier].
|
||||
* @param isScrollable if the whole content should be scrollable.
|
||||
* @param background optional background component.
|
||||
* @param topBar optional topBar.
|
||||
* @param header optional header.
|
||||
@@ -50,6 +53,7 @@ fun HeaderFooterPage(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(20.dp),
|
||||
containerColor: Color = MaterialTheme.colorScheme.background,
|
||||
isScrollable: Boolean = false,
|
||||
background: @Composable () -> Unit = {},
|
||||
topBar: @Composable () -> Unit = {},
|
||||
header: @Composable () -> Unit = {},
|
||||
@@ -63,25 +67,53 @@ fun HeaderFooterPage(
|
||||
) { padding ->
|
||||
Box {
|
||||
background()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = paddingValues)
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
) {
|
||||
// Header
|
||||
header()
|
||||
// Content
|
||||
if (isScrollable) {
|
||||
// Render in a LazyColumn
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = paddingValues)
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.imePadding()
|
||||
) {
|
||||
// Header
|
||||
item {
|
||||
header()
|
||||
}
|
||||
// Content
|
||||
item {
|
||||
content()
|
||||
}
|
||||
// Footer
|
||||
item {
|
||||
Box(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
footer()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Render in a Column
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
.padding(paddingValues = paddingValues)
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.imePadding()
|
||||
) {
|
||||
content()
|
||||
}
|
||||
// Footer
|
||||
Box(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
footer()
|
||||
// Header
|
||||
header()
|
||||
// Content
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
// Footer
|
||||
Box(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
footer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class FakeRoomListService : RoomListService {
|
||||
private val allRoomSummariesFlow = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||
private val inviteRoomSummariesFlow = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||
private val allRoomsLoadingStateFlow = MutableStateFlow<RoomList.LoadingState>(RoomList.LoadingState.NotLoaded)
|
||||
private val roomListStateFlow = MutableStateFlow<RoomListService.State>(RoomListService.State.Idle)
|
||||
private val syncIndicatorStateFlow = MutableStateFlow<RoomListService.SyncIndicator>(RoomListService.SyncIndicator.Hide)
|
||||
@@ -35,10 +34,6 @@ class FakeRoomListService : RoomListService {
|
||||
allRoomSummariesFlow.emit(roomSummaries)
|
||||
}
|
||||
|
||||
suspend fun postInviteRooms(roomSummaries: List<RoomSummary>) {
|
||||
inviteRoomSummariesFlow.emit(roomSummaries)
|
||||
}
|
||||
|
||||
suspend fun postAllRoomsLoadingState(loadingState: RoomList.LoadingState) {
|
||||
allRoomsLoadingStateFlow.emit(loadingState)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,6 @@ class StableCharSequence(initialText: CharSequence = "") {
|
||||
fun needsDisplaying(): Boolean = needsDisplaying
|
||||
|
||||
override fun toString(): String {
|
||||
return "ImmutableCharSequence(value='$value', needsDisplaying=$needsDisplaying)"
|
||||
return "StableCharSequence(value='$value', needsDisplaying=$needsDisplaying)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ class MarkdownTextInputTest {
|
||||
onTyping: (Boolean) -> Unit = {},
|
||||
onSuggestionReceived: (Suggestion?) -> Unit = {},
|
||||
) {
|
||||
rule.setContent {
|
||||
setContent {
|
||||
val style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus)
|
||||
MarkdownTextInput(
|
||||
state = state,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user