Fix user loading when returning from background
This commit is contained in:
parent
1b0afe3753
commit
c8f5a92e12
5 changed files with 85 additions and 60 deletions
|
@ -4,8 +4,10 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.findNavController
|
||||
import com.wbrawner.budget.AllowanceApplication
|
||||
import com.wbrawner.budget.AsyncState
|
||||
import com.wbrawner.budget.R
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -28,20 +30,23 @@ class SplashActivity : AppCompatActivity(), CoroutineScope {
|
|||
)
|
||||
}
|
||||
val navController = findNavController(R.id.auth_content)
|
||||
launch {
|
||||
val navId = try {
|
||||
val user = viewModel.checkForExistingCredentials()
|
||||
if (user != null) {
|
||||
(application as AllowanceApplication).currentUser = user
|
||||
R.id.mainActivity
|
||||
} else {
|
||||
R.id.loginFragment
|
||||
viewModel.state.observe(this, Observer { state ->
|
||||
when (state) {
|
||||
is AsyncState.Success -> {
|
||||
when (state.data) {
|
||||
is AuthenticationState.Authenticated -> {
|
||||
navController.navigate(R.id.mainActivity)
|
||||
finish()
|
||||
}
|
||||
is AuthenticationState.Unauthenticated -> {
|
||||
navController.navigate(R.id.loginFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
R.id.loginFragment
|
||||
}
|
||||
navController.navigate(navId)
|
||||
if (navId == R.id.mainActivity) finish()
|
||||
})
|
||||
launch {
|
||||
viewModel.checkForExistingCredentials()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,38 +2,35 @@ package com.wbrawner.budget.ui
|
|||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.wbrawner.budget.AsyncState
|
||||
import com.wbrawner.budget.AsyncViewModel
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class SplashViewModel : ViewModel() {
|
||||
class SplashViewModel : ViewModel(), AsyncViewModel<AuthenticationState> {
|
||||
override val state: MutableLiveData<AsyncState<AuthenticationState>> = MutableLiveData(AsyncState.Loading)
|
||||
|
||||
@Inject
|
||||
lateinit var budgetRepository: BudgetRepository
|
||||
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
val isLoading: MutableLiveData<Boolean> = MutableLiveData(false)
|
||||
|
||||
suspend fun checkForExistingCredentials(): User? {
|
||||
return try {
|
||||
val user = userRepository.getProfile()
|
||||
loadBudgetData()
|
||||
user
|
||||
suspend fun checkForExistingCredentials() {
|
||||
state.postValue(AsyncState.Success(AuthenticationState.Splash))
|
||||
val authState = try {
|
||||
AuthenticationState.Authenticated
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
AuthenticationState.Unauthenticated
|
||||
}
|
||||
state.postValue(AsyncState.Success(authState))
|
||||
}
|
||||
|
||||
suspend fun login(username: String, password: String): User {
|
||||
isLoading.value = true
|
||||
return try {
|
||||
val user = userRepository.login(username, password)
|
||||
fun login(username: String, password: String) = launch {
|
||||
AuthenticationState.Authenticated.also {
|
||||
loadBudgetData()
|
||||
user
|
||||
} catch (e: java.lang.Exception) {
|
||||
isLoading.value = false
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,3 +39,8 @@ class SplashViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
sealed class AuthenticationState {
|
||||
object Splash : AuthenticationState()
|
||||
object Unauthenticated : AuthenticationState()
|
||||
object Authenticated : AuthenticationState()
|
||||
}
|
|
@ -8,23 +8,17 @@ import android.view.ViewGroup
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.wbrawner.budget.AllowanceApplication
|
||||
import com.wbrawner.budget.AsyncState
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.ui.SplashViewModel
|
||||
import com.wbrawner.budget.ui.ensureNotEmpty
|
||||
import com.wbrawner.budget.ui.show
|
||||
import kotlinx.android.synthetic.main.fragment_login.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
/**
|
||||
* A simple [Fragment] subclass.
|
||||
*/
|
||||
class LoginFragment : Fragment(), CoroutineScope {
|
||||
override val coroutineContext: CoroutineContext = Dispatchers.Main
|
||||
class LoginFragment : Fragment() {
|
||||
private val viewModel: SplashViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -35,14 +29,18 @@ class LoginFragment : Fragment(), CoroutineScope {
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewModel.isLoading.observe(viewLifecycleOwner, Observer { isLoading ->
|
||||
formPrompt.show(!isLoading)
|
||||
usernameContainer.show(!isLoading)
|
||||
passwordContainer.show(!isLoading)
|
||||
submit.show(!isLoading)
|
||||
registerButton.show(!isLoading)
|
||||
forgotPasswordLink.show(!isLoading)
|
||||
progressBar.show(isLoading)
|
||||
viewModel.state.observe(viewLifecycleOwner, Observer { state ->
|
||||
when (state) {
|
||||
is AsyncState.Loading -> {
|
||||
handleLoading(true)
|
||||
}
|
||||
is AsyncState.Error -> {
|
||||
handleLoading(false)
|
||||
username.error = "Invalid username/password"
|
||||
password.error = "Invalid username/password"
|
||||
state.exception.printStackTrace()
|
||||
}
|
||||
}
|
||||
})
|
||||
password.setOnEditorActionListener { _, _, _ ->
|
||||
submit.performClick()
|
||||
|
@ -51,18 +49,7 @@ class LoginFragment : Fragment(), CoroutineScope {
|
|||
if (!username.ensureNotEmpty() || !password.ensureNotEmpty()) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
launch {
|
||||
try {
|
||||
val user = viewModel.login(username.text.toString(), password.text.toString())
|
||||
(requireActivity().application as AllowanceApplication).currentUser = user
|
||||
findNavController().navigate(R.id.mainActivity)
|
||||
activity?.finish()
|
||||
} catch (e: Exception) {
|
||||
username.error = "Invalid username/password"
|
||||
password.error = "Invalid username/password"
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
viewModel.login(username.text.toString(), password.text.toString())
|
||||
}
|
||||
val usernameString = arguments?.getString(EXTRA_USERNAME)
|
||||
val passwordString = arguments?.getString(EXTRA_PASSWORD)
|
||||
|
@ -73,6 +60,16 @@ class LoginFragment : Fragment(), CoroutineScope {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleLoading(isLoading: Boolean) {
|
||||
formPrompt.show(!isLoading)
|
||||
usernameContainer.show(!isLoading)
|
||||
passwordContainer.show(!isLoading)
|
||||
submit.show(!isLoading)
|
||||
registerButton.show(!isLoading)
|
||||
forgotPasswordLink.show(!isLoading)
|
||||
progressBar.show(isLoading)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_USERNAME = "username"
|
||||
const val EXTRA_PASSWORD = "password"
|
||||
|
|
|
@ -1,17 +1,36 @@
|
|||
package com.wbrawner.budget.lib.repository
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.wbrawner.budget.common.user.LoginRequest
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class NetworkUserRepository @Inject constructor(private val apiService: BudgetApiService) : UserRepository {
|
||||
|
||||
override suspend fun login(username: String, password: String): User =
|
||||
apiService.login(LoginRequest(username, password))
|
||||
override val currentUser: LiveData<User?> = MutableLiveData()
|
||||
|
||||
override suspend fun getProfile(): User = apiService.getProfile()
|
||||
init {
|
||||
GlobalScope.launch {
|
||||
try {
|
||||
getProfile()
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String): User =
|
||||
apiService.login(LoginRequest(username, password)).also {
|
||||
(currentUser as MutableLiveData).postValue(it)
|
||||
}
|
||||
|
||||
override suspend fun getProfile(): User = apiService.getProfile().also {
|
||||
(currentUser as MutableLiveData).postValue(it)
|
||||
}
|
||||
|
||||
override suspend fun create(newItem: User): User = apiService.newUser(newItem)
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package com.wbrawner.budget.common.user
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.wbrawner.budget.common.Repository
|
||||
|
||||
interface UserRepository : Repository<User, Long> {
|
||||
val currentUser: LiveData<User?>
|
||||
suspend fun login(username: String, password: String): User
|
||||
suspend fun getProfile(): User
|
||||
suspend fun findAll(accountId: Long? = null): List<User>
|
||||
|
|
Loading…
Reference in a new issue