Merge pull request #3049 from element-hq/feature/bma/hiddenText

Fix recovery key field hidden when keyboard is opened.
This commit is contained in:
Benoit Marty
2024-06-18 18:38:20 +02:00
committed by GitHub
21 changed files with 123 additions and 44 deletions

View File

@@ -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 = {}
)

View File

@@ -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 = {

View File

@@ -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,

View File

@@ -218,7 +218,7 @@ class VerifySelfSessionViewTest {
onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(),
onFinished: () -> Unit = EnsureNeverCalled(),
) {
rule.setContent {
setContent {
VerifySelfSessionView(
state = state,
onEnterRecoveryKey = onEnterRecoveryKey,

View File

@@ -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 = {

View File

@@ -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()
}
}
}
}

View File

@@ -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)
}

View File

@@ -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)"
}
}

View File

@@ -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,