mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-07-22 11:11:02 +00:00
Create account: user feedback when account name is already taken (#701)
* Show snackbar when account can't be created without known reason; show error when account name is taken * Don't crash on empty account name
This commit is contained in:
parent
7b2f14d148
commit
e638f5debc
|
@ -19,6 +19,7 @@ import androidx.compose.material.Icon
|
|||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.OutlinedTextField
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.SnackbarHostState
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Email
|
||||
|
@ -29,9 +30,11 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
|
@ -43,9 +46,11 @@ import at.bitfire.davdroid.servicedetection.DavResourceFinder
|
|||
import at.bitfire.davdroid.ui.composable.Assistant
|
||||
import at.bitfire.davdroid.ui.widget.ExceptionInfoDialog
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun AccountDetailsPage(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
loginInfo: LoginInfo,
|
||||
foundConfig: DavResourceFinder.Configuration,
|
||||
onBack: () -> Unit,
|
||||
|
@ -54,6 +59,9 @@ fun AccountDetailsPage(
|
|||
) {
|
||||
BackHandler(onBack = onBack)
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val resultOrNull by model.createAccountResult.observeAsState()
|
||||
var showExceptionInfo by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(resultOrNull) {
|
||||
|
@ -73,9 +81,16 @@ fun AccountDetailsPage(
|
|||
model.createAccountResult.value = null
|
||||
}
|
||||
)
|
||||
// TODO else
|
||||
}
|
||||
else
|
||||
scope.launch {
|
||||
snackbarHostState.showSnackbar(context.getString(R.string.login_account_not_created))
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
// reset result
|
||||
model.createAccountResult.value = null
|
||||
}
|
||||
|
||||
val suggestedAccountNames = foundConfig.calDAV?.emails ?: emptyList()
|
||||
|
@ -86,6 +101,7 @@ fun AccountDetailsPage(
|
|||
AccountDetailsPage_Content(
|
||||
suggestedAccountNames = suggestedAccountNames,
|
||||
accountName = accountName,
|
||||
accountNameAlreadyExists = model.accountExists(accountName).observeAsState(false).value,
|
||||
onUpdateAccountName = { accountName = it },
|
||||
onCreateAccount = {
|
||||
model.createAccount(
|
||||
|
@ -106,6 +122,7 @@ fun AccountDetailsPage(
|
|||
fun AccountDetailsPage_Content(
|
||||
suggestedAccountNames: List<String>,
|
||||
accountName: String,
|
||||
accountNameAlreadyExists: Boolean,
|
||||
onUpdateAccountName: (String) -> Unit = {},
|
||||
groupMethod: GroupMethod,
|
||||
groupMethodReadOnly: Boolean,
|
||||
|
@ -114,7 +131,8 @@ fun AccountDetailsPage_Content(
|
|||
) {
|
||||
Assistant(
|
||||
nextLabel = stringResource(R.string.login_create_account),
|
||||
onNext = onCreateAccount
|
||||
onNext = onCreateAccount,
|
||||
nextEnabled = accountName.isNotBlank() && !accountNameAlreadyExists
|
||||
) {
|
||||
Column(Modifier.padding(8.dp)) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
@ -122,10 +140,15 @@ fun AccountDetailsPage_Content(
|
|||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it }
|
||||
) {
|
||||
val accountNameLabel = if (accountNameAlreadyExists)
|
||||
stringResource(R.string.login_account_name_already_taken)
|
||||
else
|
||||
stringResource(R.string.login_account_name)
|
||||
OutlinedTextField(
|
||||
value = accountName,
|
||||
onValueChange = onUpdateAccountName,
|
||||
label = { Text(stringResource(R.string.login_account_name)) },
|
||||
label = { Text(accountNameLabel) },
|
||||
isError = accountNameAlreadyExists,
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Email
|
||||
|
@ -227,6 +250,7 @@ fun AccountDetailsPage_Content_Preview() {
|
|||
AccountDetailsPage_Content(
|
||||
suggestedAccountNames = listOf("name1", "name2@example.com"),
|
||||
accountName = "account@example.com",
|
||||
accountNameAlreadyExists = false,
|
||||
groupMethod = GroupMethod.GROUP_VCARDS,
|
||||
groupMethodReadOnly = false
|
||||
)
|
||||
|
@ -238,6 +262,7 @@ fun AccountDetailsPage_Content_Preview_With_Apostrophe() {
|
|||
AccountDetailsPage_Content(
|
||||
suggestedAccountNames = listOf("name1", "name2@example.com"),
|
||||
accountName = "account'example.com",
|
||||
accountNameAlreadyExists = true,
|
||||
groupMethod = GroupMethod.CATEGORIES,
|
||||
groupMethodReadOnly = true
|
||||
)
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
package at.bitfire.davdroid.ui.setup
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.app.Application
|
||||
import android.content.ContentResolver
|
||||
import android.provider.CalendarContract
|
||||
import androidx.core.os.CancellationSignal
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.liveData
|
||||
import androidx.lifecycle.map
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.davdroid.InvalidAccountException
|
||||
|
@ -77,6 +80,19 @@ class LoginModel @Inject constructor(
|
|||
}
|
||||
|
||||
|
||||
fun accountExists(accountName: String): LiveData<Boolean> = liveData {
|
||||
val accountType = context.getString(R.string.account_type)
|
||||
val exists =
|
||||
if (accountName.isEmpty())
|
||||
false
|
||||
else
|
||||
AccountManager.get(context)
|
||||
.getAccountsByType(accountType)
|
||||
.contains(Account(accountName, accountType))
|
||||
emit(exists)
|
||||
}
|
||||
|
||||
|
||||
interface CreateAccountResult {
|
||||
class Success(val account: Account): CreateAccountResult
|
||||
class Error(val exception: Exception?): CreateAccountResult
|
||||
|
|
|
@ -145,6 +145,7 @@ fun LoginScreen(
|
|||
foundConfig?.let {
|
||||
val context = LocalContext.current
|
||||
AccountDetailsPage(
|
||||
snackbarHostState = snackbarHostState,
|
||||
loginInfo = loginInfo,
|
||||
foundConfig = it,
|
||||
onBack = { phase = LoginActivity.Phase.LOGIN_TYPE },
|
||||
|
|
Loading…
Reference in a new issue