Remove bad usage of ElementSurface
This commit is contained in:
parent
8d87320531
commit
10763dcecc
5 changed files with 384 additions and 407 deletions
|
|
@ -18,6 +18,7 @@ package io.element.android.x
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
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.integration.NodeHost
|
||||||
import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
|
import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
|
||||||
import io.element.android.libraries.architecture.bindings
|
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.designsystem.theme.ElementTheme
|
||||||
import io.element.android.libraries.di.DaggerComponentOwner
|
import io.element.android.libraries.di.DaggerComponentOwner
|
||||||
import io.element.android.x.di.AppBindings
|
import io.element.android.x.di.AppBindings
|
||||||
|
|
@ -41,7 +41,7 @@ class MainActivity : NodeComponentActivity() {
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
setContent {
|
setContent {
|
||||||
ElementTheme {
|
ElementTheme {
|
||||||
ElementSurface(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
NodeHost(integrationPoint = appyxIntegrationPoint) {
|
NodeHost(integrationPoint = appyxIntegrationPoint) {
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
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.ElementButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.ElementCircularProgressIndicator
|
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.ElementOutlinedTextField
|
||||||
import io.element.android.libraries.designsystem.theme.components.ElementSurface
|
|
||||||
import io.element.android.libraries.testtags.TestTags
|
import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.testtags.testTag
|
import io.element.android.libraries.testtags.testTag
|
||||||
|
|
||||||
|
|
@ -68,118 +65,114 @@ fun ChangeServerView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onChangeServerSuccess: () -> Unit = {},
|
onChangeServerSuccess: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
ElementSurface(
|
val eventSink = state.eventSink
|
||||||
modifier = modifier,
|
val scrollState = rememberScrollState()
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.systemBarsPadding()
|
||||||
|
.imePadding()
|
||||||
) {
|
) {
|
||||||
val eventSink = state.eventSink
|
Column(
|
||||||
val scrollState = rememberScrollState()
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.verticalScroll(
|
||||||
.systemBarsPadding()
|
state = scrollState,
|
||||||
.imePadding()
|
)
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
Column(
|
val isError = state.changeServerAction is Async.Failure
|
||||||
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.verticalScroll(
|
.padding(top = 99.dp)
|
||||||
state = scrollState,
|
.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
|
VectorIcon(
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 99.dp)
|
.align(Alignment.Center)
|
||||||
.size(width = 81.dp, height = 73.dp)
|
.size(width = 48.dp, height = 48.dp),
|
||||||
.align(Alignment.CenterHorizontally)
|
// TODO Update with design input
|
||||||
.background(
|
resourceId = R.drawable.ic_baseline_dataset_24,
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
Text(
|
}
|
||||||
text = "A server is a home for all your data.\n" +
|
Text(
|
||||||
"You choose your server and it’s easy to make one.", // TODO "Learn more.",
|
text = "Your server",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.align(Alignment.CenterHorizontally)
|
.defaultMinSize(minHeight = 56.dp)
|
||||||
.padding(top = 16.dp),
|
.align(Alignment.CenterHorizontally)
|
||||||
textAlign = TextAlign.Center,
|
.padding(top = 38.dp),
|
||||||
fontSize = 16.sp,
|
textAlign = TextAlign.Center,
|
||||||
color = ElementTheme.colors.secondary
|
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(
|
if (state.changeServerAction is Async.Failure) {
|
||||||
value = homeserverFieldState,
|
Text(
|
||||||
modifier = Modifier
|
text = changeServerError(
|
||||||
.fillMaxWidth()
|
state.homeserver,
|
||||||
.testTag(TestTags.changeServerServer)
|
state.changeServerAction.error
|
||||||
.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(
|
color = ElementTheme.colors.error,
|
||||||
onDone = { eventSink(ChangeServerEvents.Submit) }
|
style = ElementTheme.typography.bodySmall,
|
||||||
)
|
modifier = Modifier.padding(start = 16.dp)
|
||||||
)
|
|
||||||
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)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package io.element.android.features.login.root
|
package io.element.android.features.login.root
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.ElementButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.ElementCircularProgressIndicator
|
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.ElementOutlinedTextField
|
||||||
import io.element.android.libraries.designsystem.theme.components.ElementSurface
|
|
||||||
import io.element.android.libraries.matrix.core.SessionId
|
import io.element.android.libraries.matrix.core.SessionId
|
||||||
import io.element.android.libraries.testtags.TestTags
|
import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.testtags.testTag
|
import io.element.android.libraries.testtags.testTag
|
||||||
|
|
@ -73,153 +73,149 @@ fun LoginRootScreen(
|
||||||
onLoginWithSuccess: (SessionId) -> Unit = {},
|
onLoginWithSuccess: (SessionId) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val eventSink = state.eventSink
|
val eventSink = state.eventSink
|
||||||
ElementSurface(
|
Box(
|
||||||
modifier = modifier,
|
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
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.verticalScroll(
|
||||||
.systemBarsPadding()
|
state = scrollState,
|
||||||
.imePadding()
|
)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
) {
|
) {
|
||||||
val scrollState = rememberScrollState()
|
val isError = state.loggedInState is LoggedInState.ErrorLoggingIn
|
||||||
var loginFieldState by textFieldState(stateValue = state.formState.login)
|
// Title
|
||||||
var passwordFieldState by textFieldState(stateValue = state.formState.password)
|
Text(
|
||||||
|
text = stringResource(id = StringR.string.ftue_auth_welcome_back_title),
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.verticalScroll(
|
.fillMaxWidth()
|
||||||
state = scrollState,
|
.padding(horizontal = 16.dp, vertical = 48.dp),
|
||||||
)
|
textAlign = TextAlign.Center,
|
||||||
.padding(horizontal = 16.dp),
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 24.sp,
|
||||||
|
)
|
||||||
|
// Form
|
||||||
|
Column(
|
||||||
|
// modifier = Modifier.weight(1f),
|
||||||
) {
|
) {
|
||||||
val isError = state.loggedInState is LoggedInState.ErrorLoggingIn
|
Box(
|
||||||
// Title
|
modifier = Modifier.fillMaxWidth()
|
||||||
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()
|
|
||||||
) {
|
|
||||||
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(
|
ElementOutlinedTextField(
|
||||||
value = loginFieldState,
|
value = state.homeserver,
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.fillMaxWidth()
|
onValueChange = { /* no op */ },
|
||||||
.testTag(TestTags.loginEmailUsername)
|
enabled = false,
|
||||||
.padding(top = 60.dp),
|
|
||||||
label = {
|
label = {
|
||||||
Text(text = stringResource(id = StringR.string.login_signin_username_hint))
|
Text(text = "Server")
|
||||||
},
|
|
||||||
onValueChange = {
|
|
||||||
loginFieldState = it
|
|
||||||
eventSink(LoginRootEvents.SetLogin(it))
|
|
||||||
},
|
},
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(
|
||||||
keyboardType = KeyboardType.Email,
|
keyboardType = KeyboardType.Uri,
|
||||||
imeAction = ImeAction.Next
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
var passwordVisible by remember { mutableStateOf(false) }
|
ElementButton(
|
||||||
if (state.loggedInState is LoggedInState.LoggingIn) {
|
onClick = onChangeServer,
|
||||||
// Ensure password is hidden when user submits the form
|
|
||||||
passwordVisible = false
|
|
||||||
}
|
|
||||||
ElementOutlinedTextField(
|
|
||||||
value = passwordFieldState,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.align(Alignment.CenterEnd)
|
||||||
.testTag(TestTags.loginPassword)
|
.testTag(TestTags.loginChangeServer)
|
||||||
.padding(top = 24.dp),
|
.padding(top = 8.dp, end = 8.dp),
|
||||||
onValueChange = {
|
content = {
|
||||||
passwordFieldState = it
|
Text(text = "Change")
|
||||||
eventSink(LoginRootEvents.SetPassword(it))
|
}
|
||||||
},
|
)
|
||||||
label = {
|
}
|
||||||
Text(text = "Password")
|
ElementOutlinedTextField(
|
||||||
},
|
value = loginFieldState,
|
||||||
isError = isError,
|
modifier = Modifier
|
||||||
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
|
.fillMaxWidth()
|
||||||
trailingIcon = {
|
.testTag(TestTags.loginEmailUsername)
|
||||||
val image =
|
.padding(top = 60.dp),
|
||||||
if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff
|
label = {
|
||||||
val description =
|
Text(text = stringResource(id = StringR.string.login_signin_username_hint))
|
||||||
if (passwordVisible) "Hide password" else "Show password"
|
},
|
||||||
|
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 }) {
|
IconButton(onClick = { passwordVisible = !passwordVisible }) {
|
||||||
Icon(imageVector = image, description)
|
Icon(imageVector = image, description)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(
|
||||||
keyboardType = KeyboardType.Password,
|
keyboardType = KeyboardType.Password,
|
||||||
imeAction = ImeAction.Done,
|
imeAction = ImeAction.Done,
|
||||||
),
|
),
|
||||||
keyboardActions = KeyboardActions(
|
keyboardActions = KeyboardActions(
|
||||||
onDone = { eventSink(LoginRootEvents.Submit) }
|
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)
|
|
||||||
)
|
)
|
||||||
|
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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,7 @@ import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.HorizontalPager
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
import com.google.accompanist.pager.HorizontalPagerIndicator
|
import com.google.accompanist.pager.HorizontalPagerIndicator
|
||||||
import com.google.accompanist.pager.rememberPagerState
|
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.ElementButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.ElementSurface
|
|
||||||
import io.element.android.libraries.testtags.TestTags
|
import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.testtags.testTag
|
import io.element.android.libraries.testtags.testTag
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
@ -67,60 +65,55 @@ fun OnBoardingScreen(
|
||||||
val carrouselState = remember { SplashCarouselStateFactory().create() }
|
val carrouselState = remember { SplashCarouselStateFactory().create() }
|
||||||
val nbOfPages = carrouselState.items.size
|
val nbOfPages = carrouselState.items.size
|
||||||
var key by remember { mutableStateOf(false) }
|
var key by remember { mutableStateOf(false) }
|
||||||
ElementSurface(
|
Box(
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
color = ElementTheme.colors.background,
|
.fillMaxSize()
|
||||||
|
.systemBarsPadding()
|
||||||
|
.padding(vertical = 16.dp)
|
||||||
) {
|
) {
|
||||||
Box(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize(),
|
||||||
.fillMaxSize()
|
|
||||||
.systemBarsPadding()
|
|
||||||
.padding(vertical = 16.dp)
|
|
||||||
) {
|
) {
|
||||||
Column(
|
val pagerState = rememberPagerState()
|
||||||
modifier = Modifier.fillMaxSize(),
|
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()
|
Text(text = stringResource(id = StringR.string.login_splash_submit))
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.ElementButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.ElementCircularProgressIndicator
|
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.ElementOutlinedTextField
|
||||||
import io.element.android.libraries.designsystem.theme.components.ElementSurface
|
|
||||||
import io.element.android.libraries.designsystem.utils.LogCompositions
|
import io.element.android.libraries.designsystem.utils.LogCompositions
|
||||||
import io.element.android.libraries.ui.strings.R as StringR
|
import io.element.android.libraries.ui.strings.R as StringR
|
||||||
|
|
||||||
|
|
@ -70,139 +69,135 @@ fun BugReportView(
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ElementSurface(
|
Box(
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.systemBarsPadding()
|
||||||
|
.imePadding()
|
||||||
) {
|
) {
|
||||||
Box(
|
val scrollState = rememberScrollState()
|
||||||
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.verticalScroll(
|
||||||
.systemBarsPadding()
|
state = scrollState,
|
||||||
.imePadding()
|
)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
) {
|
) {
|
||||||
val scrollState = rememberScrollState()
|
val isError = state.sending is Async.Failure
|
||||||
Column(
|
val isFormEnabled = state.sending !is Async.Loading
|
||||||
|
// Title
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = StringR.string.send_bug_report),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.verticalScroll(
|
.fillMaxWidth()
|
||||||
state = scrollState,
|
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||||
)
|
textAlign = TextAlign.Center,
|
||||||
.padding(horizontal = 16.dp),
|
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
|
ElementOutlinedTextField(
|
||||||
val isFormEnabled = state.sending !is Async.Loading
|
value = descriptionFieldState,
|
||||||
// Title
|
|
||||||
Text(
|
|
||||||
text = stringResource(id = StringR.string.send_bug_report),
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
.padding(top = 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)) },
|
|
||||||
enabled = isFormEnabled,
|
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(
|
LabelledCheckbox(
|
||||||
checked = state.formState.sendCrashLogs,
|
checked = state.formState.sendLogs,
|
||||||
onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) },
|
onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) },
|
||||||
enabled = isFormEnabled,
|
enabled = isFormEnabled,
|
||||||
text = stringResource(id = StringR.string.send_bug_report_include_crash_logs)
|
text = stringResource(id = StringR.string.send_bug_report_include_logs)
|
||||||
)
|
)
|
||||||
}
|
if (state.hasCrashLogs) {
|
||||||
LabelledCheckbox(
|
LabelledCheckbox(
|
||||||
checked = state.formState.canContact,
|
checked = state.formState.sendCrashLogs,
|
||||||
onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) },
|
onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) },
|
||||||
enabled = isFormEnabled,
|
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(
|
LabelledCheckbox(
|
||||||
checked = state.formState.sendScreenshot,
|
checked = state.formState.canContact,
|
||||||
onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) },
|
onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) },
|
||||||
enabled = isFormEnabled,
|
enabled = isFormEnabled,
|
||||||
text = stringResource(id = StringR.string.send_bug_report_include_screenshot)
|
text = stringResource(id = StringR.string.you_may_contact_me)
|
||||||
)
|
)
|
||||||
if (state.formState.sendScreenshot) {
|
if (state.screenshotUri != null) {
|
||||||
Box(
|
LabelledCheckbox(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
checked = state.formState.sendScreenshot,
|
||||||
contentAlignment = Alignment.Center
|
onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) },
|
||||||
) {
|
enabled = isFormEnabled,
|
||||||
val context = LocalContext.current
|
text = stringResource(id = StringR.string.send_bug_report_include_screenshot)
|
||||||
val model = ImageRequest.Builder(context)
|
)
|
||||||
.data(state.screenshotUri)
|
if (state.formState.sendScreenshot) {
|
||||||
.build()
|
Box(
|
||||||
AsyncImage(
|
modifier = Modifier.fillMaxWidth(),
|
||||||
modifier = Modifier.fillMaxWidth(fraction = 0.5f),
|
contentAlignment = Alignment.Center
|
||||||
model = model,
|
) {
|
||||||
contentDescription = null
|
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) {
|
// Submit
|
||||||
is Async.Loading -> {
|
ElementButton(
|
||||||
ElementCircularProgressIndicator(
|
onClick = { eventSink(BugReportEvents.SendBugReport) },
|
||||||
progress = state.sendingProgress,
|
enabled = state.submitEnabled,
|
||||||
modifier = Modifier.align(Alignment.Center)
|
modifier = Modifier
|
||||||
)
|
.fillMaxWidth()
|
||||||
}
|
.padding(vertical = 32.dp)
|
||||||
is Async.Failure -> ErrorDialog(
|
) {
|
||||||
content = state.sending.error.toString(),
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue