Skip to content

Commit

Permalink
Spruce up login screen design (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
jocmp authored Jun 22, 2024
1 parent cd2f0e0 commit 3682de4
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,4 @@ internal val articlesModule = module {
appPreferences = get()
)
}
viewModel {
UpdateAuthViewModel(get())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.jocmp.capy.Account
import com.jocmp.capyreader.R
import com.jocmp.capyreader.ui.components.DialogCard
import com.jocmp.capyreader.ui.login.LoginViewModel
import org.koin.compose.koinInject
import org.koin.core.parameter.parametersOf

@Composable
fun UpdateAuthDialog(
onDismissRequest: () -> Unit,
onSuccess: (message: String) -> Unit,
viewModel: UpdateAuthViewModel = koinInject()
account: Account = koinInject(),
viewModel: LoginViewModel = koinInject(parameters = { parametersOf(account) })
) {
val successMessage = stringResource(R.string.update_auth_success_message)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fun UpdateAuthView(
showError: Boolean = false,
) {
val errorMessage = if (showError) {
stringResource(R.string.update_auth_error_message)
stringResource(R.string.auth_error_message)
} else {
null
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module

val loginModule = module {
viewModel {
viewModel { parameters ->
LoginViewModel(
account = parameters.getOrNull(),
accountManager = get(),
appPreferences = get()
)
Expand Down
61 changes: 12 additions & 49 deletions app/src/main/java/com/jocmp/capyreader/ui/login/LoginScreen.kt
Original file line number Diff line number Diff line change
@@ -1,62 +1,25 @@
package com.jocmp.capyreader.ui.login

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import org.koin.compose.koinInject

@Composable
fun LoginScreen(
viewModel: LoginViewModel = koinInject(),
onSuccess: () -> Unit,
) {
val (username, setUsername) = rememberSaveable {
mutableStateOf("")
}

val (password, setPassword) = rememberSaveable {
mutableStateOf("")
}

val login = {
viewModel.login(username, password) { result ->
result.fold(
onSuccess = { onSuccess() },
onFailure = {}
)
}
}

Scaffold { padding ->
Column(Modifier.padding(padding)) {
AuthFields(
username = username,
password = password,
onUsernameChange = setUsername,
onPasswordChange = setPassword,
onSubmit = login,
)
}
}

LaunchedEffect(Unit) {
Firebase.crashlytics.setCrashlyticsCollectionEnabled(true)
}
}


@Preview
@Composable
private fun LoginScreenPreview() {
LoginScreen(
onSuccess = {}
LoginView(
onUsernameChange = viewModel::setUsername,
onPasswordChange = viewModel::setPassword,
onSubmit = {
viewModel.submit {
onSuccess()
}
},
username = viewModel.username,
password = viewModel.password,
loading = viewModel.loading,
showError = viewModel.showError
)
}
74 changes: 74 additions & 0 deletions app/src/main/java/com/jocmp/capyreader/ui/login/LoginView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.jocmp.capyreader.ui.login

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.jocmp.capyreader.R

@Composable
fun LoginView(
onUsernameChange: (username: String) -> Unit = {},
onPasswordChange: (password: String) -> Unit = {},
onSubmit: () -> Unit = {},
username: String,
password: String,
loading: Boolean = false,
showError: Boolean = false,
) {
val errorMessage = if (showError) {
stringResource(R.string.auth_error_message)
} else {
null
}

Scaffold { padding ->
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.widthIn(max = 400.dp)
.padding(horizontal = 16.dp)
) {
Text(
text = stringResource(R.string.login_title),
style = typography.headlineMedium,
)
AuthFields(
username = username,
password = password,
onUsernameChange = onUsernameChange,
onPasswordChange = onPasswordChange,
onSubmit = onSubmit,
loading = loading,
errorMessage = errorMessage
)
}
}
}
}

@Preview
@Composable
private fun LoginViewPreview() {
LoginView(
username = "[email protected]",
password = "",
)
}
79 changes: 61 additions & 18 deletions app/src/main/java/com/jocmp/capyreader/ui/login/LoginViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,45 +1,88 @@
package com.jocmp.capyreader.ui.login

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.jocmp.capy.Account
import com.jocmp.capy.AccountManager
import com.jocmp.capy.accounts.verifyCredentials
import com.jocmp.capyreader.common.AppPreferences
import com.jocmp.capyreader.common.Async
import com.jocmp.capyreader.loadAccountModules
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class LoginViewModel(
private val account: Account? = null,
private val accountManager: AccountManager,
private val appPreferences: AppPreferences,
) : ViewModel() {
fun login(
username: String,
password: String,
onComplete: (result: Result<Unit>) -> Unit
) {
viewModelScope.launch {
val result = verifyCredentials(username = username, password = password)
private var _username by mutableStateOf(account?.preferences?.username?.get().orEmpty())
private var _password by mutableStateOf("")
private var _result by mutableStateOf<Async<Unit>>(Async.Uninitialized)

if (result) {
val accountID = accountManager.createAccount(
username = username,
password = password,
)
val username: String
get() = _username

selectAccount(accountID)
val password: String
get() = _password

loadAccountModules()
val loading: Boolean
get() = _result is Async.Loading

Result.success(Unit)
val showError: Boolean
get() = _result is Async.Failure

fun setUsername(username: String) {
_username = username
}

fun setPassword(password: String) {
_password = password
}

fun submit(onSuccess: () -> Unit) {
if (username.isBlank() || password.isBlank()) {
_result = Async.Failure(loginError())
}

viewModelScope.launch(Dispatchers.IO) {
_result = Async.Loading

val isSuccessful = verifyCredentials(username = username, password = password)

if (isSuccessful) {
withContext(Dispatchers.Main) {
updateOrCreateAccount()
onSuccess()
}
} else {
Result.failure(Exception("Couldn't log in"))
_result = Async.Failure(loginError())
}
}
}

private fun updateOrCreateAccount() {
if (account == null) {
val accountID = accountManager.createAccount(
username = username,
password = password,
)

selectAccount(accountID)

loadAccountModules()
} else {
account.preferences.password.set(password)
}
}

private fun selectAccount(id: String) {
appPreferences.articleID.delete()
appPreferences.filter.delete()
appPreferences.accountID.set(id)
}

private fun loginError() = Error("Error logging in")
}
3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
<string name="auth_fields_log_in_button">Log In</string>
<string name="settings_top_bar_title">Settings</string>
<string name="update_auth_title">Log in to Feedbin</string>
<string name="update_auth_error_message">Invalid email or password</string>
<string name="auth_error_message">Invalid email or password</string>
<string name="update_auth_success_message">Successfully logged in</string>
<string name="login_title">Log in to Feedbin</string>
</resources>

0 comments on commit 3682de4

Please sign in to comment.