From 1c6aa43beaa4b90894f679adfbc57d9f2e29bdb4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Jun 2023 17:09:38 +0200 Subject: [PATCH] Design iteration on bug report screen. --- .../rageshake/impl/bugreport/BugReportNode.kt | 4 +- .../rageshake/impl/bugreport/BugReportView.kt | 117 +++++++----------- .../components/preferences/PreferenceRow.kt | 60 +++++++++ .../theme/components/OutlinedTextField.kt | 2 + 4 files changed, 110 insertions(+), 73 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceRow.kt diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt index b5c20c1ab2..a087300f4e 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt @@ -26,7 +26,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint -import io.element.android.features.rageshake.impl.bugreport.BugReportPresenter import io.element.android.libraries.di.AppScope @ContributesNode(AppScope::class) @@ -42,7 +41,8 @@ class BugReportNode @AssistedInject constructor( BugReportView( state = state, modifier = modifier, - onDone = this::onDone + onBackPressed = { navigateUp() }, + onDone = this::onDone, ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 60c36e0a29..c4e5b78960 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -17,16 +17,11 @@ package io.element.android.features.rageshake.impl.bugreport import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -35,21 +30,20 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import coil.compose.AsyncImage import coil.request.ImageRequest import io.element.android.features.rageshake.impl.R import io.element.android.libraries.architecture.Async -import io.element.android.libraries.designsystem.components.LabelledCheckbox import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.form.textFieldState +import io.element.android.libraries.designsystem.components.preferences.PreferenceRow +import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch +import io.element.android.libraries.designsystem.components.preferences.PreferenceView import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground @@ -63,8 +57,9 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun BugReportView( state: BugReportState, + onDone: () -> Unit, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onDone: () -> Unit = { }, ) { LogCompositions(tag = "Rageshake", msg = "Root") val eventSink = state.eventSink @@ -75,56 +70,27 @@ fun BugReportView( } return } - Box( - modifier = modifier - .fillMaxSize() - .systemBarsPadding() - .imePadding() - ) { - Column( - modifier = Modifier - .verticalScroll(state = rememberScrollState()) - .padding(horizontal = 16.dp), + + Box(modifier = modifier) { + PreferenceView( + title = stringResource(id = CommonStrings.common_report_a_bug), + onBackPressed = onBackPressed ) { - val isError = state.sending is Async.Failure val isFormEnabled = state.sending !is Async.Loading - // Title - Text( - text = stringResource(id = CommonStrings.action_report_bug), - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - fontSize = 24.sp, - color = MaterialTheme.colorScheme.primary, - ) - // Form - Text( - text = stringResource(id = R.string.screen_bug_report_editor_description), - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp), - fontSize = 16.sp, - color = MaterialTheme.colorScheme.primary, - ) var descriptionFieldState by textFieldState( stateValue = state.formState.description ) - Column( - // modifier = Modifier.weight(1f), - ) { + Spacer(modifier = Modifier.height(16.dp)) + PreferenceRow { OutlinedTextField( value = descriptionFieldState, - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp), + modifier = Modifier.fillMaxWidth(), enabled = isFormEnabled, label = { Text(text = stringResource(id = R.string.screen_bug_report_editor_placeholder)) }, supportingText = { - Text(text = stringResource(id = R.string.screen_bug_report_editor_supporting)) + Text(text = stringResource(id = R.string.screen_bug_report_editor_description)) }, onValueChange = { descriptionFieldState = it @@ -134,35 +100,37 @@ fun BugReportView( keyboardType = KeyboardType.Text, imeAction = ImeAction.Next ), + minLines = 3, // TODO Error text too short ) } - LabelledCheckbox( - checked = state.formState.sendLogs, + Spacer(modifier = Modifier.height(16.dp)) + PreferenceSwitch( + isChecked = state.formState.sendLogs, onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) }, enabled = isFormEnabled, - text = stringResource(id = R.string.screen_bug_report_include_logs) + title = stringResource(id = R.string.screen_bug_report_include_logs), ) if (state.hasCrashLogs) { - LabelledCheckbox( - checked = state.formState.sendCrashLogs, + PreferenceSwitch( + isChecked = state.formState.sendCrashLogs, onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) }, enabled = isFormEnabled, - text = stringResource(id = R.string.screen_bug_report_include_crash_logs) + title = stringResource(id = R.string.screen_bug_report_include_crash_logs), ) } - LabelledCheckbox( - checked = state.formState.canContact, + PreferenceSwitch( + isChecked = state.formState.canContact, onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) }, enabled = isFormEnabled, - text = stringResource(id = R.string.screen_bug_report_contact_me) + title = stringResource(id = R.string.screen_bug_report_contact_me) ) if (state.screenshotUri != null) { - LabelledCheckbox( - checked = state.formState.sendScreenshot, + PreferenceSwitch( + isChecked = state.formState.sendScreenshot, onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) }, enabled = isFormEnabled, - text = stringResource(id = R.string.screen_bug_report_include_screenshot) + title = stringResource(id = R.string.screen_bug_report_include_screenshot) ) if (state.formState.sendScreenshot) { Box( @@ -183,16 +151,19 @@ fun BugReportView( } } // Submit - Button( - onClick = { eventSink(BugReportEvents.SendBugReport) }, - enabled = state.submitEnabled, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 32.dp) - ) { - Text(text = stringResource(id = CommonStrings.action_send)) + PreferenceRow { + Button( + onClick = { eventSink(BugReportEvents.SendBugReport) }, + enabled = state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp, bottom = 16.dp) + ) { + Text(text = stringResource(id = CommonStrings.action_send)) + } } } + when (state.sending) { is Async.Loading -> { // Indeterminate indicator, to avoid the freeze effect if the connection takes time to initialize. @@ -219,5 +190,9 @@ fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) st @Composable private fun ContentToPreview(state: BugReportState) { - BugReportView(state = state) + BugReportView( + state = state, + onDone = {}, + onBackPressed = {}, + ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceRow.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceRow.kt new file mode 100644 index 0000000000..ef1c6ac09a --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceRow.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.components.preferences + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import io.element.android.libraries.designsystem.preview.ElementThemedPreview +import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.theme.components.Text + +/** + * Simple Row with which follow design for preferences. + */ +@Composable +fun PreferenceRow( + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit, +) { + Row( + modifier = modifier + .padding(horizontal = preferencePaddingHorizontal) + .heightIn(min = preferenceMinHeight) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + content() + } +} + +@Preview(group = PreviewGroup.Preferences) +@Composable +internal fun PreferenceRowPreview() = ElementThemedPreview { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + PreferenceRow { + Text(text = "Content") + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt index 358ca2abab..2c7318c447 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedTextField.kt @@ -68,6 +68,7 @@ fun OutlinedTextField( keyboardActions: KeyboardActions = KeyboardActions.Default, singleLine: Boolean = false, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + minLines: Int = 1, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, shape: Shape = OutlinedTextFieldDefaults.shape, colors: TextFieldColors = OutlinedTextFieldDefaults.colors() @@ -90,6 +91,7 @@ fun OutlinedTextField( keyboardActions = keyboardActions, singleLine = singleLine, maxLines = maxLines, + minLines = minLines, interactionSource = interactionSource, shape = shape, colors = colors,