From 10763dcecc2e911842db96d630d129f4b0eb116f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Jan 2023 18:01:27 +0100 Subject: [PATCH] Remove bad usage of ElementSurface --- .../io/element/android/x/MainActivity.kt | 4 +- .../login/changeserver/ChangeServerView.kt | 199 +++++++------- .../features/login/root/LoginRootScreen.kt | 260 +++++++++--------- .../features/onboarding/OnBoardingScreen.kt | 99 ++++--- .../rageshake/bugreport/BugReportView.kt | 229 ++++++++------- 5 files changed, 384 insertions(+), 407 deletions(-) diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index eb249d5b75..c37a35476d 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -18,6 +18,7 @@ package io.element.android.x import android.os.Bundle import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen @@ -25,7 +26,6 @@ import androidx.core.view.WindowCompat import com.bumble.appyx.core.integration.NodeHost import com.bumble.appyx.core.integrationpoint.NodeComponentActivity import io.element.android.libraries.architecture.bindings -import io.element.android.libraries.designsystem.theme.components.ElementSurface import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.x.di.AppBindings @@ -41,7 +41,7 @@ class MainActivity : NodeComponentActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) setContent { ElementTheme { - ElementSurface( + Box( modifier = Modifier.fillMaxSize(), ) { NodeHost(integrationPoint = appyxIntegrationPoint) { diff --git a/features/login/src/main/kotlin/io/element/android/features/login/changeserver/ChangeServerView.kt b/features/login/src/main/kotlin/io/element/android/features/login/changeserver/ChangeServerView.kt index e81e6d7d14..4ffeb42def 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/changeserver/ChangeServerView.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/changeserver/ChangeServerView.kt @@ -33,9 +33,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -58,7 +56,6 @@ import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.designsystem.theme.components.ElementButton import io.element.android.libraries.designsystem.theme.components.ElementCircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.ElementOutlinedTextField -import io.element.android.libraries.designsystem.theme.components.ElementSurface import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag @@ -68,118 +65,114 @@ fun ChangeServerView( modifier: Modifier = Modifier, onChangeServerSuccess: () -> Unit = {}, ) { - ElementSurface( - modifier = modifier, + val eventSink = state.eventSink + val scrollState = rememberScrollState() + Box( + modifier = modifier + .fillMaxSize() + .systemBarsPadding() + .imePadding() ) { - val eventSink = state.eventSink - val scrollState = rememberScrollState() - Box( + Column( modifier = Modifier - .fillMaxSize() - .systemBarsPadding() - .imePadding() + .verticalScroll( + state = scrollState, + ) + .padding(horizontal = 16.dp) ) { - Column( + val isError = state.changeServerAction is Async.Failure + Box( modifier = Modifier - .verticalScroll( - state = scrollState, + .padding(top = 99.dp) + .size(width = 81.dp, height = 73.dp) + .align(Alignment.CenterHorizontally) + .background( + color = ElementTheme.colors.surfaceVariant, + shape = RoundedCornerShape(32.dp) ) - .padding(horizontal = 16.dp) ) { - val isError = state.changeServerAction is Async.Failure - Box( + VectorIcon( modifier = Modifier - .padding(top = 99.dp) - .size(width = 81.dp, height = 73.dp) - .align(Alignment.CenterHorizontally) - .background( - color = ElementTheme.colors.surfaceVariant, - shape = RoundedCornerShape(32.dp) - ) - ) { - VectorIcon( - modifier = Modifier - .align(Alignment.Center) - .size(width = 48.dp, height = 48.dp), - // TODO Update with design input - resourceId = R.drawable.ic_baseline_dataset_24, - ) - } - Text( - text = "Your server", - modifier = Modifier - .fillMaxWidth() - .defaultMinSize(minHeight = 56.dp) - .align(Alignment.CenterHorizontally) - .padding(top = 38.dp), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - fontSize = 24.sp, + .align(Alignment.Center) + .size(width = 48.dp, height = 48.dp), + // TODO Update with design input + resourceId = R.drawable.ic_baseline_dataset_24, ) - Text( - text = "A server is a home for all your data.\n" + - "You choose your server and it’s easy to make one.", // TODO "Learn more.", - modifier = Modifier - .fillMaxWidth() - .align(Alignment.CenterHorizontally) - .padding(top = 16.dp), - textAlign = TextAlign.Center, - fontSize = 16.sp, - color = ElementTheme.colors.secondary + } + Text( + text = "Your server", + modifier = Modifier + .fillMaxWidth() + .defaultMinSize(minHeight = 56.dp) + .align(Alignment.CenterHorizontally) + .padding(top = 38.dp), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + fontSize = 24.sp, + ) + Text( + text = "A server is a home for all your data.\n" + + "You choose your server and it’s easy to make one.", // TODO "Learn more.", + modifier = Modifier + .fillMaxWidth() + .align(Alignment.CenterHorizontally) + .padding(top = 16.dp), + textAlign = TextAlign.Center, + fontSize = 16.sp, + color = ElementTheme.colors.secondary + ) + var homeserverFieldState by textFieldState(stateValue = state.homeserver) + ElementOutlinedTextField( + value = homeserverFieldState, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.changeServerServer) + .padding(top = 200.dp), + onValueChange = { + homeserverFieldState = it + eventSink(ChangeServerEvents.SetServer(it)) + }, + label = { + Text(text = "Server") + }, + isError = isError, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { eventSink(ChangeServerEvents.Submit) } ) - var homeserverFieldState by textFieldState(stateValue = state.homeserver) - ElementOutlinedTextField( - value = homeserverFieldState, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.changeServerServer) - .padding(top = 200.dp), - onValueChange = { - homeserverFieldState = it - eventSink(ChangeServerEvents.SetServer(it)) - }, - label = { - Text(text = "Server") - }, - isError = isError, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Password, - imeAction = ImeAction.Done, + ) + if (state.changeServerAction is Async.Failure) { + Text( + text = changeServerError( + state.homeserver, + state.changeServerAction.error ), - keyboardActions = KeyboardActions( - onDone = { eventSink(ChangeServerEvents.Submit) } - ) - ) - if (state.changeServerAction is Async.Failure) { - Text( - text = changeServerError( - state.homeserver, - state.changeServerAction.error - ), - color = ElementTheme.colors.error, - style = ElementTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp) - ) - } - ElementButton( - onClick = { eventSink(ChangeServerEvents.Submit) }, - enabled = state.submitEnabled, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.changeServerContinue) - .padding(top = 44.dp) - ) { - Text(text = "Continue") - } - if (state.changeServerAction is Async.Success) { - onChangeServerSuccess() - } - } - if (state.changeServerAction is Async.Loading) { - ElementCircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) + color = ElementTheme.colors.error, + style = ElementTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp) ) } + ElementButton( + onClick = { eventSink(ChangeServerEvents.Submit) }, + enabled = state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.changeServerContinue) + .padding(top = 44.dp) + ) { + Text(text = "Continue") + } + if (state.changeServerAction is Async.Success) { + onChangeServerSuccess() + } + } + if (state.changeServerAction is Async.Loading) { + ElementCircularProgressIndicator( + modifier = Modifier.align(Alignment.Center) + ) } } } diff --git a/features/login/src/main/kotlin/io/element/android/features/login/root/LoginRootScreen.kt b/features/login/src/main/kotlin/io/element/android/features/login/root/LoginRootScreen.kt index 5dd53a1254..71ca4ebe15 100644 --- a/features/login/src/main/kotlin/io/element/android/features/login/root/LoginRootScreen.kt +++ b/features/login/src/main/kotlin/io/element/android/features/login/root/LoginRootScreen.kt @@ -18,6 +18,7 @@ package io.element.android.features.login.root +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -59,7 +60,6 @@ import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.designsystem.theme.components.ElementButton import io.element.android.libraries.designsystem.theme.components.ElementCircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.ElementOutlinedTextField -import io.element.android.libraries.designsystem.theme.components.ElementSurface import io.element.android.libraries.matrix.core.SessionId import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag @@ -73,153 +73,149 @@ fun LoginRootScreen( onLoginWithSuccess: (SessionId) -> Unit = {}, ) { val eventSink = state.eventSink - ElementSurface( - modifier = modifier, + Box( + modifier = modifier + .fillMaxSize() + .systemBarsPadding() + .imePadding() ) { - Box( + val scrollState = rememberScrollState() + var loginFieldState by textFieldState(stateValue = state.formState.login) + var passwordFieldState by textFieldState(stateValue = state.formState.password) + + Column( modifier = Modifier - .fillMaxSize() - .systemBarsPadding() - .imePadding() + .verticalScroll( + state = scrollState, + ) + .padding(horizontal = 16.dp), ) { - val scrollState = rememberScrollState() - var loginFieldState by textFieldState(stateValue = state.formState.login) - var passwordFieldState by textFieldState(stateValue = state.formState.password) - - Column( + val isError = state.loggedInState is LoggedInState.ErrorLoggingIn + // Title + Text( + text = stringResource(id = StringR.string.ftue_auth_welcome_back_title), modifier = Modifier - .verticalScroll( - state = scrollState, - ) - .padding(horizontal = 16.dp), + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 48.dp), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + fontSize = 24.sp, + ) + // Form + Column( + // modifier = Modifier.weight(1f), ) { - val isError = state.loggedInState is LoggedInState.ErrorLoggingIn - // Title - Text( - text = stringResource(id = StringR.string.ftue_auth_welcome_back_title), - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 48.dp), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - fontSize = 24.sp, - ) - // Form - Column( - // modifier = Modifier.weight(1f), + Box( + modifier = Modifier.fillMaxWidth() ) { - Box( - modifier = Modifier.fillMaxWidth() - ) { - ElementOutlinedTextField( - value = state.homeserver, - modifier = Modifier.fillMaxWidth(), - onValueChange = { /* no op */ }, - enabled = false, - label = { - Text(text = "Server") - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Uri, - ), - ) - ElementButton( - onClick = onChangeServer, - modifier = Modifier - .align(Alignment.CenterEnd) - .testTag(TestTags.loginChangeServer) - .padding(top = 8.dp, end = 8.dp), - content = { - Text(text = "Change") - } - ) - } ElementOutlinedTextField( - value = loginFieldState, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginEmailUsername) - .padding(top = 60.dp), + value = state.homeserver, + modifier = Modifier.fillMaxWidth(), + onValueChange = { /* no op */ }, + enabled = false, label = { - Text(text = stringResource(id = StringR.string.login_signin_username_hint)) - }, - onValueChange = { - loginFieldState = it - eventSink(LoginRootEvents.SetLogin(it)) + Text(text = "Server") }, keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Email, - imeAction = ImeAction.Next + keyboardType = KeyboardType.Uri, ), ) - var passwordVisible by remember { mutableStateOf(false) } - if (state.loggedInState is LoggedInState.LoggingIn) { - // Ensure password is hidden when user submits the form - passwordVisible = false - } - ElementOutlinedTextField( - value = passwordFieldState, + ElementButton( + onClick = onChangeServer, modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginPassword) - .padding(top = 24.dp), - onValueChange = { - passwordFieldState = it - eventSink(LoginRootEvents.SetPassword(it)) - }, - label = { - Text(text = "Password") - }, - isError = isError, - visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), - trailingIcon = { - val image = - if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff - val description = - if (passwordVisible) "Hide password" else "Show password" + .align(Alignment.CenterEnd) + .testTag(TestTags.loginChangeServer) + .padding(top = 8.dp, end = 8.dp), + content = { + Text(text = "Change") + } + ) + } + ElementOutlinedTextField( + value = loginFieldState, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginEmailUsername) + .padding(top = 60.dp), + label = { + Text(text = stringResource(id = StringR.string.login_signin_username_hint)) + }, + onValueChange = { + loginFieldState = it + eventSink(LoginRootEvents.SetLogin(it)) + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Email, + imeAction = ImeAction.Next + ), + ) + var passwordVisible by remember { mutableStateOf(false) } + if (state.loggedInState is LoggedInState.LoggingIn) { + // Ensure password is hidden when user submits the form + passwordVisible = false + } + ElementOutlinedTextField( + value = passwordFieldState, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginPassword) + .padding(top = 24.dp), + onValueChange = { + passwordFieldState = it + eventSink(LoginRootEvents.SetPassword(it)) + }, + label = { + Text(text = "Password") + }, + isError = isError, + visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + val image = + if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff + val description = + if (passwordVisible) "Hide password" else "Show password" - IconButton(onClick = { passwordVisible = !passwordVisible }) { - Icon(imageVector = image, description) - } - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Password, - imeAction = ImeAction.Done, - ), - keyboardActions = KeyboardActions( - onDone = { eventSink(LoginRootEvents.Submit) } - ), - ) - if (state.loggedInState is LoggedInState.ErrorLoggingIn) { - Text( - text = loginError(state.formState, state.loggedInState.failure), - color = ElementTheme.colors.error, - style = ElementTheme.typography.bodySmall, - modifier = Modifier.padding(start = 16.dp) - ) - } - } - // Submit - ElementButton( - onClick = { eventSink(LoginRootEvents.Submit) }, - enabled = state.submitEnabled, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginContinue) - .padding(vertical = 32.dp) - ) { - Text(text = "Continue") - } - when (val loggedInState = state.loggedInState) { - is LoggedInState.LoggedIn -> onLoginWithSuccess(loggedInState.sessionId) - else -> Unit - } - } - if (state.loggedInState is LoggedInState.LoggingIn) { - ElementCircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon(imageVector = image, description) + } + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { eventSink(LoginRootEvents.Submit) } + ), ) + if (state.loggedInState is LoggedInState.ErrorLoggingIn) { + Text( + text = loginError(state.formState, state.loggedInState.failure), + color = ElementTheme.colors.error, + style = ElementTheme.typography.bodySmall, + modifier = Modifier.padding(start = 16.dp) + ) + } } + // Submit + ElementButton( + onClick = { eventSink(LoginRootEvents.Submit) }, + enabled = state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginContinue) + .padding(vertical = 32.dp) + ) { + Text(text = "Continue") + } + when (val loggedInState = state.loggedInState) { + is LoggedInState.LoggedIn -> onLoginWithSuccess(loggedInState.sessionId) + else -> Unit + } + } + if (state.loggedInState is LoggedInState.LoggingIn) { + ElementCircularProgressIndicator( + modifier = Modifier.align(Alignment.Center) + ) } } } diff --git a/features/onboarding/src/main/kotlin/io/element/android/features/onboarding/OnBoardingScreen.kt b/features/onboarding/src/main/kotlin/io/element/android/features/onboarding/OnBoardingScreen.kt index 684d7a02ce..6cab7dd091 100644 --- a/features/onboarding/src/main/kotlin/io/element/android/features/onboarding/OnBoardingScreen.kt +++ b/features/onboarding/src/main/kotlin/io/element/android/features/onboarding/OnBoardingScreen.kt @@ -47,9 +47,7 @@ import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.HorizontalPagerIndicator import com.google.accompanist.pager.rememberPagerState -import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.designsystem.theme.components.ElementButton -import io.element.android.libraries.designsystem.theme.components.ElementSurface import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import kotlinx.coroutines.delay @@ -67,60 +65,55 @@ fun OnBoardingScreen( val carrouselState = remember { SplashCarouselStateFactory().create() } val nbOfPages = carrouselState.items.size var key by remember { mutableStateOf(false) } - ElementSurface( - modifier = modifier, - color = ElementTheme.colors.background, + Box( + modifier = modifier + .fillMaxSize() + .systemBarsPadding() + .padding(vertical = 16.dp) ) { - Box( - modifier = Modifier - .fillMaxSize() - .systemBarsPadding() - .padding(vertical = 16.dp) + Column( + modifier = Modifier.fillMaxSize(), ) { - Column( - modifier = Modifier.fillMaxSize(), + val pagerState = rememberPagerState() + LaunchedEffect(key) { + launch { + delay(3_000) + pagerState.animateScrollToPage((pagerState.currentPage + 1) % nbOfPages) + // https://stackoverflow.com/questions/73714228/accompanist-pager-animatescrolltopage-doesnt-scroll-to-next-page-correctly + key = !key + } + } + LaunchedEffect(pagerState) { + // Collect from the pager state a snapshotFlow reading the currentPage + snapshotFlow { pagerState.currentPage }.collect { page -> + onPageChanged(page) + } + } + HorizontalPager( + modifier = Modifier.weight(1f), + count = nbOfPages, + state = pagerState, + ) { page -> + // Our page content + OnBoardingPage(carrouselState.items[page]) + } + HorizontalPagerIndicator( + pagerState = pagerState, + modifier = Modifier + .align(CenterHorizontally) + .padding(16.dp), + ) + ElementButton( + onClick = { + onSignIn() + }, + enabled = true, + modifier = Modifier + .align(CenterHorizontally) + .testTag(TestTags.onBoardingSignIn) + .padding(top = 16.dp) ) { - val pagerState = rememberPagerState() - LaunchedEffect(key) { - launch { - delay(3_000) - pagerState.animateScrollToPage((pagerState.currentPage + 1) % nbOfPages) - // https://stackoverflow.com/questions/73714228/accompanist-pager-animatescrolltopage-doesnt-scroll-to-next-page-correctly - key = !key - } - } - LaunchedEffect(pagerState) { - // Collect from the pager state a snapshotFlow reading the currentPage - snapshotFlow { pagerState.currentPage }.collect { page -> - onPageChanged(page) - } - } - HorizontalPager( - modifier = Modifier.weight(1f), - count = nbOfPages, - state = pagerState, - ) { page -> - // Our page content - OnBoardingPage(carrouselState.items[page]) - } - HorizontalPagerIndicator( - pagerState = pagerState, - modifier = Modifier - .align(CenterHorizontally) - .padding(16.dp), - ) - ElementButton( - onClick = { - onSignIn() - }, - enabled = true, - modifier = Modifier - .align(CenterHorizontally) - .testTag(TestTags.onBoardingSignIn) - .padding(top = 16.dp) - ) { - Text(text = stringResource(id = StringR.string.login_splash_submit)) - } + Text(text = stringResource(id = StringR.string.login_splash_submit)) } } } diff --git a/features/rageshake/src/main/kotlin/io/element/android/features/rageshake/bugreport/BugReportView.kt b/features/rageshake/src/main/kotlin/io/element/android/features/rageshake/bugreport/BugReportView.kt index 75b0a5228e..a81f961ac3 100644 --- a/features/rageshake/src/main/kotlin/io/element/android/features/rageshake/bugreport/BugReportView.kt +++ b/features/rageshake/src/main/kotlin/io/element/android/features/rageshake/bugreport/BugReportView.kt @@ -51,7 +51,6 @@ import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.theme.components.ElementButton import io.element.android.libraries.designsystem.theme.components.ElementCircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.ElementOutlinedTextField -import io.element.android.libraries.designsystem.theme.components.ElementSurface import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.ui.strings.R as StringR @@ -70,139 +69,135 @@ fun BugReportView( } return } - ElementSurface( - modifier = modifier, + Box( + modifier = modifier + .fillMaxSize() + .systemBarsPadding() + .imePadding() ) { - Box( + val scrollState = rememberScrollState() + Column( modifier = Modifier - .fillMaxSize() - .systemBarsPadding() - .imePadding() + .verticalScroll( + state = scrollState, + ) + .padding(horizontal = 16.dp), ) { - val scrollState = rememberScrollState() - Column( + val isError = state.sending is Async.Failure + val isFormEnabled = state.sending !is Async.Loading + // Title + Text( + text = stringResource(id = StringR.string.send_bug_report), modifier = Modifier - .verticalScroll( - state = scrollState, - ) - .padding(horizontal = 16.dp), + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 16.dp), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + fontSize = 24.sp, + ) + // Form + Text( + text = stringResource(id = StringR.string.send_bug_report_description), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 16.dp), + fontSize = 16.sp, + ) + var descriptionFieldState by textFieldState( + stateValue = state.formState.description + ) + Column( + // modifier = Modifier.weight(1f), ) { - val isError = state.sending is Async.Failure - val isFormEnabled = state.sending !is Async.Loading - // Title - Text( - text = stringResource(id = StringR.string.send_bug_report), + ElementOutlinedTextField( + value = descriptionFieldState, modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - fontSize = 24.sp, - ) - // Form - Text( - text = stringResource(id = StringR.string.send_bug_report_description), - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp), - fontSize = 16.sp, - ) - var descriptionFieldState by textFieldState( - stateValue = state.formState.description - ) - Column( - // modifier = Modifier.weight(1f), - ) { - ElementOutlinedTextField( - value = descriptionFieldState, - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp), - enabled = isFormEnabled, - label = { - Text(text = stringResource(id = StringR.string.send_bug_report_placeholder)) - }, - supportingText = { - Text(text = stringResource(id = StringR.string.send_bug_report_description_in_english)) - }, - onValueChange = { - descriptionFieldState = it - eventSink(BugReportEvents.SetDescription(it)) - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Text, - imeAction = ImeAction.Next - ), - // TODO Error text too short - ) - } - LabelledCheckbox( - checked = state.formState.sendLogs, - onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) }, + .padding(top = 16.dp), enabled = isFormEnabled, - text = stringResource(id = StringR.string.send_bug_report_include_logs) + label = { + Text(text = stringResource(id = StringR.string.send_bug_report_placeholder)) + }, + supportingText = { + Text(text = stringResource(id = StringR.string.send_bug_report_description_in_english)) + }, + onValueChange = { + descriptionFieldState = it + eventSink(BugReportEvents.SetDescription(it)) + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Next + ), + // TODO Error text too short ) - if (state.hasCrashLogs) { - LabelledCheckbox( - checked = state.formState.sendCrashLogs, - onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) }, - enabled = isFormEnabled, - text = stringResource(id = StringR.string.send_bug_report_include_crash_logs) - ) - } + } + LabelledCheckbox( + checked = state.formState.sendLogs, + onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) }, + enabled = isFormEnabled, + text = stringResource(id = StringR.string.send_bug_report_include_logs) + ) + if (state.hasCrashLogs) { LabelledCheckbox( - checked = state.formState.canContact, - onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) }, + checked = state.formState.sendCrashLogs, + onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) }, enabled = isFormEnabled, - text = stringResource(id = StringR.string.you_may_contact_me) + text = stringResource(id = StringR.string.send_bug_report_include_crash_logs) ) - if (state.screenshotUri != null) { - LabelledCheckbox( - checked = state.formState.sendScreenshot, - onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) }, - enabled = isFormEnabled, - text = stringResource(id = StringR.string.send_bug_report_include_screenshot) - ) - if (state.formState.sendScreenshot) { - Box( - modifier = Modifier.fillMaxWidth(), - contentAlignment = Alignment.Center - ) { - val context = LocalContext.current - val model = ImageRequest.Builder(context) - .data(state.screenshotUri) - .build() - AsyncImage( - modifier = Modifier.fillMaxWidth(fraction = 0.5f), - model = model, - contentDescription = null - ) - } + } + LabelledCheckbox( + checked = state.formState.canContact, + onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) }, + enabled = isFormEnabled, + text = stringResource(id = StringR.string.you_may_contact_me) + ) + if (state.screenshotUri != null) { + LabelledCheckbox( + checked = state.formState.sendScreenshot, + onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) }, + enabled = isFormEnabled, + text = stringResource(id = StringR.string.send_bug_report_include_screenshot) + ) + if (state.formState.sendScreenshot) { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + val context = LocalContext.current + val model = ImageRequest.Builder(context) + .data(state.screenshotUri) + .build() + AsyncImage( + modifier = Modifier.fillMaxWidth(fraction = 0.5f), + model = model, + contentDescription = null + ) } } - // Submit - ElementButton( - onClick = { eventSink(BugReportEvents.SendBugReport) }, - enabled = state.submitEnabled, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 32.dp) - ) { - Text(text = stringResource(id = StringR.string.action_send)) - } } - when (state.sending) { - is Async.Loading -> { - ElementCircularProgressIndicator( - progress = state.sendingProgress, - modifier = Modifier.align(Alignment.Center) - ) - } - is Async.Failure -> ErrorDialog( - content = state.sending.error.toString(), + // Submit + ElementButton( + onClick = { eventSink(BugReportEvents.SendBugReport) }, + enabled = state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 32.dp) + ) { + Text(text = stringResource(id = StringR.string.action_send)) + } + } + when (state.sending) { + is Async.Loading -> { + ElementCircularProgressIndicator( + progress = state.sendingProgress, + modifier = Modifier.align(Alignment.Center) ) - else -> Unit } + is Async.Failure -> ErrorDialog( + content = state.sending.error.toString(), + ) + else -> Unit } } }