mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-28 01:16:27 +02:00
Re-use existing LoginPage component in account switcher
This commit is contained in:
@@ -1,70 +1,42 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.navigation
|
package com.vitorpamplona.amethyst.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.text.ClickableText
|
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.Button
|
|
||||||
import androidx.compose.material.ButtonDefaults
|
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.ModalBottomSheetState
|
|
||||||
import androidx.compose.material.OutlinedTextField
|
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.TextButton
|
import androidx.compose.material.TextButton
|
||||||
|
import androidx.compose.material.TopAppBar
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Logout
|
import androidx.compose.material.icons.filled.Logout
|
||||||
import androidx.compose.material.icons.outlined.Visibility
|
|
||||||
import androidx.compose.material.icons.outlined.VisibilityOff
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.autofill.AutofillNode
|
|
||||||
import androidx.compose.ui.autofill.AutofillType
|
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||||
import androidx.compose.ui.layout.boundsInWindow
|
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
|
||||||
import androidx.compose.ui.platform.LocalAutofill
|
|
||||||
import androidx.compose.ui.platform.LocalAutofillTree
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.input.PasswordVisualTransformation
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import androidx.compose.ui.text.input.VisualTransformation
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
@@ -75,15 +47,13 @@ import com.vitorpamplona.amethyst.ui.components.ResizeImage
|
|||||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginPage
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountSwitchBottomSheet(
|
fun AccountSwitchBottomSheet(
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountStateViewModel: AccountStateViewModel
|
||||||
sheetState: ModalBottomSheetState
|
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val accounts = LocalPreferences.findAllLocalAccounts()
|
val accounts = LocalPreferences.findAllLocalAccounts()
|
||||||
|
|
||||||
@@ -117,7 +87,8 @@ fun AccountSwitchBottomSheet(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
accountStateViewModel.login(acc.npub)
|
accountStateViewModel.login(acc.npub)
|
||||||
}
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
AsyncImageProxy(
|
AsyncImageProxy(
|
||||||
model = ResizeImage(acc.profilePicture, 64.dp),
|
model = ResizeImage(acc.profilePicture, 64.dp),
|
||||||
@@ -141,13 +112,18 @@ fun AccountSwitchBottomSheet(
|
|||||||
if (current) {
|
if (current) {
|
||||||
Text("✓")
|
Text("✓")
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { accountStateViewModel.logOff(acc.npub) }
|
onClick = { accountStateViewModel.logOff(acc.npub) }
|
||||||
) {
|
) {
|
||||||
Icon(imageVector = Icons.Default.Logout, "Logout")
|
Icon(
|
||||||
|
imageVector = Icons.Default.Logout,
|
||||||
|
contentDescription = "Logout",
|
||||||
|
tint = MaterialTheme.colors.onSurface
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,126 +146,21 @@ fun AccountSwitchBottomSheet(
|
|||||||
properties = DialogProperties(usePlatformDefaultWidth = false)
|
properties = DialogProperties(usePlatformDefaultWidth = false)
|
||||||
) {
|
) {
|
||||||
Surface(modifier = Modifier.fillMaxSize()) {
|
Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
Column(
|
Box {
|
||||||
modifier = Modifier
|
LoginPage(accountStateViewModel, isFirstLogin = false)
|
||||||
.fillMaxHeight()
|
TopAppBar(
|
||||||
.background(MaterialTheme.colors.surface),
|
title = { Text(text = "Add New Account") },
|
||||||
verticalArrangement = Arrangement.Center,
|
navigationIcon = {
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
IconButton(onClick = { popupExpanded = false }) {
|
||||||
) {
|
|
||||||
val key = remember { mutableStateOf(TextFieldValue("")) }
|
|
||||||
var errorMessage by remember { mutableStateOf("") }
|
|
||||||
var showPassword by remember {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
val autofillNode = AutofillNode(
|
|
||||||
autofillTypes = listOf(AutofillType.Password),
|
|
||||||
onFill = { key.value = TextFieldValue(it) }
|
|
||||||
)
|
|
||||||
val autofill = LocalAutofill.current
|
|
||||||
LocalAutofillTree.current += autofillNode
|
|
||||||
|
|
||||||
OutlinedTextField(
|
|
||||||
modifier = Modifier
|
|
||||||
.onGloballyPositioned { coordinates ->
|
|
||||||
autofillNode.boundingBox = coordinates.boundsInWindow()
|
|
||||||
}
|
|
||||||
.onFocusChanged { focusState ->
|
|
||||||
autofill?.run {
|
|
||||||
if (focusState.isFocused) {
|
|
||||||
requestAutofillForNode(autofillNode)
|
|
||||||
} else {
|
|
||||||
cancelAutofillForNode(autofillNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
value = key.value,
|
|
||||||
onValueChange = { key.value = it },
|
|
||||||
keyboardOptions = KeyboardOptions(
|
|
||||||
autoCorrect = false,
|
|
||||||
keyboardType = KeyboardType.Password,
|
|
||||||
imeAction = ImeAction.Go
|
|
||||||
),
|
|
||||||
placeholder = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.nsec_npub_hex_private_key),
|
|
||||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailingIcon = {
|
|
||||||
IconButton(onClick = { showPassword = !showPassword }) {
|
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (showPassword) Icons.Outlined.VisibilityOff else Icons.Outlined.Visibility,
|
imageVector = Icons.Default.ArrowBack,
|
||||||
contentDescription = if (showPassword) {
|
contentDescription = "Back",
|
||||||
stringResource(R.string.show_password)
|
tint = MaterialTheme.colors.onSurface
|
||||||
} else {
|
|
||||||
stringResource(
|
|
||||||
R.string.hide_password
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
|
backgroundColor = Color.Transparent,
|
||||||
keyboardActions = KeyboardActions(
|
elevation = 0.dp
|
||||||
onGo = {
|
|
||||||
try {
|
|
||||||
accountStateViewModel.login(key.value.text)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
errorMessage = context.getString(R.string.invalid_key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (errorMessage.isNotBlank()) {
|
|
||||||
Text(
|
|
||||||
text = errorMessage,
|
|
||||||
color = MaterialTheme.colors.error,
|
|
||||||
style = MaterialTheme.typography.caption
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
if (key.value.text.isBlank()) {
|
|
||||||
errorMessage = context.getString(R.string.key_is_required)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
accountStateViewModel.login(key.value.text)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
errorMessage = context.getString(R.string.invalid_key)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shape = RoundedCornerShape(35.dp),
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(50.dp),
|
|
||||||
colors = ButtonDefaults
|
|
||||||
.buttonColors(
|
|
||||||
backgroundColor = MaterialTheme.colors.primary
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.login))
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
ClickableText(
|
|
||||||
text = AnnotatedString(stringResource(R.string.generate_a_new_key)),
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(20.dp)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
onClick = {
|
|
||||||
accountStateViewModel.newKey()
|
|
||||||
},
|
|
||||||
style = TextStyle(
|
|
||||||
fontSize = 14.sp,
|
|
||||||
textDecoration = TextDecoration.Underline,
|
|
||||||
color = MaterialTheme.colors.primary,
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.MainScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.MainScreen
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginPage
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountScreen(accountStateViewModel: AccountStateViewModel, startingPage: String?) {
|
fun AccountScreen(accountStateViewModel: AccountStateViewModel, startingPage: String?) {
|
||||||
@@ -17,7 +18,7 @@ fun AccountScreen(accountStateViewModel: AccountStateViewModel, startingPage: St
|
|||||||
Crossfade(targetState = accountState, animationSpec = tween(durationMillis = 100)) { state ->
|
Crossfade(targetState = accountState, animationSpec = tween(durationMillis = 100)) { state ->
|
||||||
when (state) {
|
when (state) {
|
||||||
is AccountState.LoggedOff -> {
|
is AccountState.LoggedOff -> {
|
||||||
LoginPage(accountStateViewModel)
|
LoginPage(accountStateViewModel, isFirstLogin = true)
|
||||||
}
|
}
|
||||||
is AccountState.LoggedIn -> {
|
is AccountState.LoggedIn -> {
|
||||||
MainScreen(AccountViewModel(state.account), accountStateViewModel, startingPage)
|
MainScreen(AccountViewModel(state.account), accountStateViewModel, startingPage)
|
||||||
|
@@ -47,7 +47,7 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
|
|||||||
ModalBottomSheetLayout(
|
ModalBottomSheetLayout(
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
sheetContent = {
|
sheetContent = {
|
||||||
AccountSwitchBottomSheet(accountViewModel = accountViewModel, accountStateViewModel = accountStateViewModel, sheetState = sheetState)
|
AccountSwitchBottomSheet(accountViewModel = accountViewModel, accountStateViewModel = accountStateViewModel)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.screen
|
package com.vitorpamplona.amethyst.ui.screen.loggedOff
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -36,14 +36,18 @@ import androidx.compose.ui.text.style.TextDecoration
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginPage(accountViewModel: AccountStateViewModel) {
|
fun LoginPage(
|
||||||
|
accountViewModel: AccountStateViewModel,
|
||||||
|
isFirstLogin: Boolean
|
||||||
|
) {
|
||||||
val key = remember { mutableStateOf(TextFieldValue("")) }
|
val key = remember { mutableStateOf(TextFieldValue("")) }
|
||||||
var errorMessage by remember { mutableStateOf("") }
|
var errorMessage by remember { mutableStateOf("") }
|
||||||
val acceptedTerms = remember { mutableStateOf(false) }
|
val acceptedTerms = remember { mutableStateOf(!isFirstLogin) }
|
||||||
var termsAcceptanceIsRequired by remember { mutableStateOf("") }
|
var termsAcceptanceIsRequired by remember { mutableStateOf("") }
|
||||||
val uri = LocalUriHandler.current
|
val uri = LocalUriHandler.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -147,48 +151,50 @@ fun LoginPage(accountViewModel: AccountStateViewModel) {
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
if (isFirstLogin) {
|
||||||
Checkbox(
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
checked = acceptedTerms.value,
|
Checkbox(
|
||||||
onCheckedChange = { acceptedTerms.value = it }
|
checked = acceptedTerms.value,
|
||||||
)
|
onCheckedChange = { acceptedTerms.value = it }
|
||||||
|
)
|
||||||
|
|
||||||
val regularText =
|
val regularText =
|
||||||
SpanStyle(color = MaterialTheme.colors.onBackground)
|
SpanStyle(color = MaterialTheme.colors.onBackground)
|
||||||
|
|
||||||
val clickableTextStyle =
|
val clickableTextStyle =
|
||||||
SpanStyle(color = MaterialTheme.colors.primary)
|
SpanStyle(color = MaterialTheme.colors.primary)
|
||||||
|
|
||||||
val annotatedTermsString = buildAnnotatedString {
|
val annotatedTermsString = buildAnnotatedString {
|
||||||
withStyle(regularText) {
|
withStyle(regularText) {
|
||||||
append(stringResource(R.string.i_accept_the))
|
append(stringResource(R.string.i_accept_the))
|
||||||
}
|
|
||||||
|
|
||||||
withStyle(clickableTextStyle) {
|
|
||||||
pushStringAnnotation("openTerms", "")
|
|
||||||
append(stringResource(R.string.terms_of_use))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ClickableText(
|
|
||||||
text = annotatedTermsString
|
|
||||||
) { spanOffset ->
|
|
||||||
annotatedTermsString.getStringAnnotations(spanOffset, spanOffset)
|
|
||||||
.firstOrNull()
|
|
||||||
?.also { span ->
|
|
||||||
if (span.tag == "openTerms") {
|
|
||||||
runCatching { uri.openUri("https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md") }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (termsAcceptanceIsRequired.isNotBlank()) {
|
withStyle(clickableTextStyle) {
|
||||||
Text(
|
pushStringAnnotation("openTerms", "")
|
||||||
text = termsAcceptanceIsRequired,
|
append(stringResource(R.string.terms_of_use))
|
||||||
color = MaterialTheme.colors.error,
|
}
|
||||||
style = MaterialTheme.typography.caption
|
}
|
||||||
)
|
|
||||||
|
ClickableText(
|
||||||
|
text = annotatedTermsString
|
||||||
|
) { spanOffset ->
|
||||||
|
annotatedTermsString.getStringAnnotations(spanOffset, spanOffset)
|
||||||
|
.firstOrNull()
|
||||||
|
?.also { span ->
|
||||||
|
if (span.tag == "openTerms") {
|
||||||
|
runCatching { uri.openUri("https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (termsAcceptanceIsRequired.isNotBlank()) {
|
||||||
|
Text(
|
||||||
|
text = termsAcceptanceIsRequired,
|
||||||
|
color = MaterialTheme.colors.error,
|
||||||
|
style = MaterialTheme.typography.caption
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
Reference in New Issue
Block a user