Add loading and error dialogs for authentication.
This commit is contained in:
		
							parent
							
								
									b07bfd45d3
								
							
						
					
					
						commit
						59d84963a9
					
				
					 11 changed files with 296 additions and 127 deletions
				
			
		| 
						 | 
				
			
			@ -22,7 +22,7 @@ class BibLibClient : IBibLibClient {
 | 
			
		|||
    ///////////////////////////////////
 | 
			
		||||
    // region BibLib webservice Auth
 | 
			
		||||
 | 
			
		||||
    override fun updateBearerToken(token: String?) {
 | 
			
		||||
    override fun updateBearerToken(token: String) {
 | 
			
		||||
        interceptor.token = token
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ interface IBibLibClient {
 | 
			
		|||
 | 
			
		||||
    val service: IBibLibWebServiceAPI
 | 
			
		||||
 | 
			
		||||
    fun updateBearerToken(token: String?)
 | 
			
		||||
    fun updateBearerToken(token: String)
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val BASE_URL = "https://bib.bibulle.fr"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,100 +0,0 @@
 | 
			
		|||
package com.pixelized.biblib.ui.composable.items
 | 
			
		||||
 | 
			
		||||
import androidx.compose.animation.Crossfade
 | 
			
		||||
import androidx.compose.foundation.background
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.layout.*
 | 
			
		||||
import androidx.compose.material.Card
 | 
			
		||||
import androidx.compose.material.CircularProgressIndicator
 | 
			
		||||
import androidx.compose.material.MaterialTheme
 | 
			
		||||
import androidx.compose.material.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.alpha
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.biblib.R
 | 
			
		||||
import com.pixelized.biblib.ui.theme.BibLibTheme
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
fun WaitingComposableLightPreview() {
 | 
			
		||||
    BibLibTheme(darkTheme = false) {
 | 
			
		||||
        WaitingComposable(
 | 
			
		||||
            visible = true,
 | 
			
		||||
            message = stringResource(id = R.string.loading)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
fun WaitingComposableDarkPreview() {
 | 
			
		||||
    BibLibTheme(darkTheme = true) {
 | 
			
		||||
        WaitingComposable(
 | 
			
		||||
            visible = true,
 | 
			
		||||
            message = stringResource(id = R.string.loading)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun WaitingComposable(
 | 
			
		||||
    visible: Boolean,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    message: String? = null
 | 
			
		||||
) {
 | 
			
		||||
    Crossfade(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        targetState = visible
 | 
			
		||||
    ) {
 | 
			
		||||
        if (it) {
 | 
			
		||||
            Box(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .fillMaxWidth()
 | 
			
		||||
                    .fillMaxHeight()
 | 
			
		||||
                    .clickable {  }
 | 
			
		||||
            ) {
 | 
			
		||||
                Box(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .fillMaxWidth()
 | 
			
		||||
                        .fillMaxHeight()
 | 
			
		||||
                        .alpha(.25f)
 | 
			
		||||
                        .background(Color.Black)
 | 
			
		||||
                )
 | 
			
		||||
                Card(
 | 
			
		||||
                    elevation = 8.dp,
 | 
			
		||||
                    modifier = Modifier.align(Alignment.Center)
 | 
			
		||||
                ) {
 | 
			
		||||
                    Column(
 | 
			
		||||
                        modifier = Modifier
 | 
			
		||||
                            .width(200.dp)
 | 
			
		||||
                            .padding(16.dp)
 | 
			
		||||
                    ) {
 | 
			
		||||
                        CircularProgressIndicator(
 | 
			
		||||
                            modifier = Modifier
 | 
			
		||||
                                .align(Alignment.CenterHorizontally)
 | 
			
		||||
                                .padding(16.dp)
 | 
			
		||||
                        )
 | 
			
		||||
                        if (message?.isNotEmpty() == true) {
 | 
			
		||||
                            val typography = MaterialTheme.typography
 | 
			
		||||
                            Text(
 | 
			
		||||
                                modifier = Modifier.align(Alignment.CenterHorizontally),
 | 
			
		||||
                                style = typography.body1,
 | 
			
		||||
                                textAlign = TextAlign.Center,
 | 
			
		||||
                                text = message
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            Box {}
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
package com.pixelized.biblib.ui.composable.items.dialog
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.height
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.layout.width
 | 
			
		||||
import androidx.compose.material.Card
 | 
			
		||||
import androidx.compose.material.Icon
 | 
			
		||||
import androidx.compose.material.MaterialTheme
 | 
			
		||||
import androidx.compose.material.Text
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.sharp.ErrorOutline
 | 
			
		||||
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.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.biblib.R
 | 
			
		||||
import com.pixelized.biblib.ui.theme.BibLibTheme
 | 
			
		||||
import com.pixelized.biblib.utils.exception.NoBearerException
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun ErrorCard(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    message: String = stringResource(id = R.string.error_generic),
 | 
			
		||||
    exception: Exception? = null,
 | 
			
		||||
) {
 | 
			
		||||
    Card(elevation = 8.dp, modifier = modifier) {
 | 
			
		||||
        Column(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .width(200.dp)
 | 
			
		||||
                .padding(16.dp)
 | 
			
		||||
        ) {
 | 
			
		||||
            Icon(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .width(72.dp)
 | 
			
		||||
                    .height(72.dp)
 | 
			
		||||
                    .align(Alignment.CenterHorizontally)
 | 
			
		||||
                    .padding(16.dp),
 | 
			
		||||
                tint = MaterialTheme.colors.error,
 | 
			
		||||
                imageVector = Icons.Sharp.ErrorOutline,
 | 
			
		||||
                contentDescription = "error"
 | 
			
		||||
            )
 | 
			
		||||
            val typography = MaterialTheme.typography
 | 
			
		||||
            Text(
 | 
			
		||||
                modifier = Modifier.align(Alignment.CenterHorizontally),
 | 
			
		||||
                style = typography.body1,
 | 
			
		||||
                textAlign = TextAlign.Center,
 | 
			
		||||
                text = message
 | 
			
		||||
            )
 | 
			
		||||
            if (exception != null) {
 | 
			
		||||
                Text(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .padding(top = 8.dp)
 | 
			
		||||
                        .align(Alignment.CenterHorizontally),
 | 
			
		||||
                    style = typography.caption,
 | 
			
		||||
                    textAlign = TextAlign.Center,
 | 
			
		||||
                    text = exception::class.java.simpleName
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
private fun ErrorCardLightPreview() {
 | 
			
		||||
    BibLibTheme(darkTheme = false) {
 | 
			
		||||
        ErrorCard(exception = NoBearerException())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
private fun ErrorCardDarkPreview() {
 | 
			
		||||
    BibLibTheme(darkTheme = true) {
 | 
			
		||||
        ErrorCard(exception = NoBearerException())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
package com.pixelized.biblib.ui.composable.items.dialog
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.layout.width
 | 
			
		||||
import androidx.compose.material.Card
 | 
			
		||||
import androidx.compose.material.CircularProgressIndicator
 | 
			
		||||
import androidx.compose.material.MaterialTheme
 | 
			
		||||
import androidx.compose.material.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.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.biblib.R
 | 
			
		||||
import com.pixelized.biblib.ui.theme.BibLibTheme
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun LoadingCard(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    message: String? = null
 | 
			
		||||
) {
 | 
			
		||||
    Card(elevation = 8.dp, modifier = modifier) {
 | 
			
		||||
        Column(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .width(200.dp)
 | 
			
		||||
                .padding(16.dp)
 | 
			
		||||
        ) {
 | 
			
		||||
            CircularProgressIndicator(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .align(Alignment.CenterHorizontally)
 | 
			
		||||
                    .padding(16.dp)
 | 
			
		||||
            )
 | 
			
		||||
            if (message?.isNotEmpty() == true) {
 | 
			
		||||
                val typography = MaterialTheme.typography
 | 
			
		||||
                Text(
 | 
			
		||||
                    modifier = Modifier.align(Alignment.CenterHorizontally),
 | 
			
		||||
                    style = typography.body1,
 | 
			
		||||
                    textAlign = TextAlign.Center,
 | 
			
		||||
                    text = message
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
private fun LoadingCardLightPreview() {
 | 
			
		||||
    BibLibTheme(darkTheme = false) {
 | 
			
		||||
        LoadingCard(
 | 
			
		||||
            message = stringResource(id = R.string.loading)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Composable
 | 
			
		||||
private fun LoadingCardDarkPreview() {
 | 
			
		||||
    BibLibTheme(darkTheme = true) {
 | 
			
		||||
        LoadingCard(
 | 
			
		||||
            message = stringResource(id = R.string.loading)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
package com.pixelized.biblib.ui.composable.items.dialog
 | 
			
		||||
 | 
			
		||||
import androidx.compose.animation.Crossfade
 | 
			
		||||
import androidx.compose.foundation.background
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.BoxScope
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxHeight
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.alpha
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun CrossFadeOverlay(
 | 
			
		||||
    visible: Boolean,
 | 
			
		||||
    content: @Composable BoxScope.() -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    Crossfade(targetState = visible) {
 | 
			
		||||
        if (it) {
 | 
			
		||||
            Box(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .fillMaxWidth()
 | 
			
		||||
                    .fillMaxHeight()
 | 
			
		||||
                    .clickable { }
 | 
			
		||||
            ) {
 | 
			
		||||
                // Transparent background.
 | 
			
		||||
                Box(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .fillMaxWidth()
 | 
			
		||||
                        .fillMaxHeight()
 | 
			
		||||
                        .alpha(.25f)
 | 
			
		||||
                        .background(Color.Black)
 | 
			
		||||
                )
 | 
			
		||||
                // Overlay content.
 | 
			
		||||
                content()
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            Box {}
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -30,12 +30,15 @@ import androidx.compose.ui.tooling.preview.Preview
 | 
			
		|||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.lifecycle.viewmodel.compose.viewModel
 | 
			
		||||
import com.pixelized.biblib.R
 | 
			
		||||
import com.pixelized.biblib.ui.composable.items.WaitingComposable
 | 
			
		||||
import com.pixelized.biblib.ui.composable.items.dialog.CrossFadeOverlay
 | 
			
		||||
import com.pixelized.biblib.ui.composable.items.dialog.ErrorCard
 | 
			
		||||
import com.pixelized.biblib.ui.composable.items.dialog.LoadingCard
 | 
			
		||||
import com.pixelized.biblib.ui.theme.BibLibTheme
 | 
			
		||||
import com.pixelized.biblib.ui.viewmodel.authentication.AuthenticationViewModel
 | 
			
		||||
import com.pixelized.biblib.ui.viewmodel.authentication.IAuthentication
 | 
			
		||||
import com.pixelized.biblib.ui.viewmodel.navigation.INavigation
 | 
			
		||||
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
 | 
			
		||||
import com.pixelized.biblib.utils.exception.NoBearerException
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
| 
						 | 
				
			
			@ -49,14 +52,15 @@ fun LoginScreenComposable(
 | 
			
		|||
            .fillMaxHeight()
 | 
			
		||||
    ) {
 | 
			
		||||
        authentication.PrepareLoginWithGoogle()
 | 
			
		||||
        LoginScreenContentComposable(navigation, authentication)
 | 
			
		||||
        LoginScreenWaitingComposable(authentication)
 | 
			
		||||
        LoginScreenNavigationComposable(navigation, authentication)
 | 
			
		||||
 | 
			
		||||
        LoginScreenContentComposable(authentication)
 | 
			
		||||
        LoginScreenDialogComposable(authentication)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun LoginScreenContentComposable(
 | 
			
		||||
    navigation: INavigation,
 | 
			
		||||
    authentication: IAuthentication,
 | 
			
		||||
) {
 | 
			
		||||
    val typography = MaterialTheme.typography
 | 
			
		||||
| 
						 | 
				
			
			@ -142,12 +146,40 @@ private fun LoginScreenContentComposable(
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun LoginScreenWaitingComposable(authentication: IAuthentication) {
 | 
			
		||||
private fun LoginScreenDialogComposable(
 | 
			
		||||
    authentication: IAuthentication
 | 
			
		||||
) {
 | 
			
		||||
    val state = authentication.state.observeAsState()
 | 
			
		||||
    WaitingComposable(
 | 
			
		||||
        visible = state.value is IAuthentication.State.Loading,
 | 
			
		||||
        message = stringResource(id = R.string.loading)
 | 
			
		||||
    )
 | 
			
		||||
    CrossFadeOverlay(
 | 
			
		||||
        visible = (state.value is IAuthentication.State.Initial).not()
 | 
			
		||||
    ) {
 | 
			
		||||
        when (val currentState = state.value) {
 | 
			
		||||
            is IAuthentication.State.Error -> ErrorCard(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .align(Alignment.Center)
 | 
			
		||||
                    .clickable { authentication.clearState() },
 | 
			
		||||
                message = stringResource(id = R.string.error_generic),
 | 
			
		||||
                exception = currentState.exception
 | 
			
		||||
            )
 | 
			
		||||
            is IAuthentication.State.Connect,
 | 
			
		||||
            is IAuthentication.State.Loading -> LoadingCard(
 | 
			
		||||
                modifier = Modifier.align(Alignment.Center),
 | 
			
		||||
                message = stringResource(id = R.string.loading)
 | 
			
		||||
            )
 | 
			
		||||
            else -> Box {}
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun LoginScreenNavigationComposable(
 | 
			
		||||
    navigation: INavigation,
 | 
			
		||||
    authentication: IAuthentication
 | 
			
		||||
) {
 | 
			
		||||
    val state = authentication.state.observeAsState()
 | 
			
		||||
    if (state.value == IAuthentication.State.Connect) {
 | 
			
		||||
        navigation.navigateTo(INavigation.Screen.MainScreen)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
| 
						 | 
				
			
			@ -237,12 +269,24 @@ fun LoginScreenComposablePreview() {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview
 | 
			
		||||
@Preview(name = "Loading")
 | 
			
		||||
@Composable
 | 
			
		||||
fun LoginScreenComposableWaitingPreview() {
 | 
			
		||||
    BibLibTheme {
 | 
			
		||||
        val navigationViewModel = INavigation.Mock()
 | 
			
		||||
        val authenticationViewModel = IAuthentication.Mock(true)
 | 
			
		||||
        val state = IAuthentication.State.Loading
 | 
			
		||||
        val authenticationViewModel = IAuthentication.Mock(state)
 | 
			
		||||
        LoginScreenComposable(navigationViewModel, authenticationViewModel)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Preview(name = "Error")
 | 
			
		||||
@Composable
 | 
			
		||||
fun LoginScreenComposableErrorPreview() {
 | 
			
		||||
    BibLibTheme {
 | 
			
		||||
        val navigationViewModel = INavigation.Mock()
 | 
			
		||||
        val state = IAuthentication.State.Error(NoBearerException())
 | 
			
		||||
        val authenticationViewModel = IAuthentication.Mock(state)
 | 
			
		||||
        LoginScreenComposable(navigationViewModel, authenticationViewModel)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
package com.pixelized.biblib.ui.viewmodel.authentication
 | 
			
		||||
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import androidx.activity.compose.rememberLauncherForActivityResult
 | 
			
		||||
import androidx.activity.result.ActivityResultLauncher
 | 
			
		||||
import androidx.activity.result.contract.ActivityResultContracts
 | 
			
		||||
| 
						 | 
				
			
			@ -14,12 +13,12 @@ import com.google.android.gms.auth.api.signin.GoogleSignIn
 | 
			
		|||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
 | 
			
		||||
import com.google.android.gms.common.api.ApiException
 | 
			
		||||
import com.pixelized.biblib.data.network.query.AuthLoginQuery
 | 
			
		||||
import com.pixelized.biblib.utils.injection.inject
 | 
			
		||||
import com.pixelized.biblib.network.client.IBibLibClient
 | 
			
		||||
import com.pixelized.biblib.repository.credential.ICredentialRepository
 | 
			
		||||
import com.pixelized.biblib.repository.googlesignin.IGoogleSingInRepository
 | 
			
		||||
import com.pixelized.biblib.ui.viewmodel.authentication.IAuthentication.State
 | 
			
		||||
import com.pixelized.biblib.utils.exception.MissingTokenException
 | 
			
		||||
import com.pixelized.biblib.utils.injection.inject
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
| 
						 | 
				
			
			@ -61,10 +60,13 @@ class AuthenticationViewModel : ViewModel(), IAuthentication {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    override fun updateRememberCredential(rememberCredential: Boolean) {
 | 
			
		||||
        _rememberCredential.postValue(rememberCredential)
 | 
			
		||||
        viewModelScope.launch {
 | 
			
		||||
            _rememberCredential.postValue(rememberCredential)
 | 
			
		||||
            credentialRepository.rememberCredential = rememberCredential
 | 
			
		||||
            if (rememberCredential.not()) {
 | 
			
		||||
            if (rememberCredential) {
 | 
			
		||||
                credentialRepository.login = login.value
 | 
			
		||||
                credentialRepository.password = password.value
 | 
			
		||||
            } else {
 | 
			
		||||
                credentialRepository.login = null
 | 
			
		||||
                credentialRepository.password = null
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -75,24 +77,37 @@ class AuthenticationViewModel : ViewModel(), IAuthentication {
 | 
			
		|||
        viewModelScope.launch {
 | 
			
		||||
            _state.postValue(State.Loading)
 | 
			
		||||
            delay(3000)
 | 
			
		||||
            _state.postValue(State.Initial)
 | 
			
		||||
            _state.postValue(State.Error(MissingTokenException()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun clearState() {
 | 
			
		||||
        _state.postValue(State.Initial)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun login() {
 | 
			
		||||
        viewModelScope.launch(Dispatchers.IO) {
 | 
			
		||||
            // TODO : validation !
 | 
			
		||||
            if (rememberCredential.value == true) {
 | 
			
		||||
                credentialRepository.login = login.value
 | 
			
		||||
                credentialRepository.password = password.value
 | 
			
		||||
            } else {
 | 
			
		||||
                credentialRepository.login = null
 | 
			
		||||
                credentialRepository.password = null
 | 
			
		||||
            }
 | 
			
		||||
            // TODO : validation !
 | 
			
		||||
            val query = AuthLoginQuery(
 | 
			
		||||
                username = login.value,
 | 
			
		||||
                password = password.value
 | 
			
		||||
            )
 | 
			
		||||
            // TODO : Repository (token management & co)
 | 
			
		||||
            val response = client.service.login(query)
 | 
			
		||||
            Log.e("pouet", response.toString())
 | 
			
		||||
            _state.postValue(State.Loading)
 | 
			
		||||
            try {
 | 
			
		||||
                val response = client.service.login(query)
 | 
			
		||||
                val idToken = response.token ?: throw MissingTokenException()
 | 
			
		||||
                client.updateBearerToken(idToken)
 | 
			
		||||
                _state.postValue(State.Connect)
 | 
			
		||||
            } catch (exception: Exception) {
 | 
			
		||||
                _state.postValue(State.Error(exception))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +120,8 @@ class AuthenticationViewModel : ViewModel(), IAuthentication {
 | 
			
		|||
                val task = GoogleSignIn.getSignedInAccountFromIntent(it.data)
 | 
			
		||||
                val account: GoogleSignInAccount? = task.getResult(ApiException::class.java)
 | 
			
		||||
                val idToken = account?.idToken ?: throw MissingTokenException()
 | 
			
		||||
                _state.postValue(State.Connect(idToken))
 | 
			
		||||
                client.updateBearerToken(idToken)
 | 
			
		||||
                _state.postValue(State.Connect)
 | 
			
		||||
            } catch (exception: Exception) {
 | 
			
		||||
                _state.postValue(State.Error(exception))
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ interface IAuthentication {
 | 
			
		|||
    fun updateLoginField(login: String)
 | 
			
		||||
    fun updatePasswordField(password: String)
 | 
			
		||||
    fun updateRememberCredential(rememberCredential: Boolean)
 | 
			
		||||
    fun clearState()
 | 
			
		||||
 | 
			
		||||
    fun register()
 | 
			
		||||
    fun login()
 | 
			
		||||
| 
						 | 
				
			
			@ -24,13 +25,12 @@ interface IAuthentication {
 | 
			
		|||
    sealed class State {
 | 
			
		||||
        object Initial : State()
 | 
			
		||||
        object Loading : State()
 | 
			
		||||
        data class Connect(val token: String) : State()
 | 
			
		||||
        object Connect : State()
 | 
			
		||||
        data class Error(val exception: Exception) : State()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class Mock(waiting: Boolean = false) : IAuthentication {
 | 
			
		||||
        override val state: LiveData<State> =
 | 
			
		||||
            MutableLiveData(if (waiting) State.Loading else State.Initial)
 | 
			
		||||
    class Mock(state: State = State.Initial) : IAuthentication {
 | 
			
		||||
        override val state: LiveData<State> = MutableLiveData(state)
 | 
			
		||||
        override val login: LiveData<String?> = MutableLiveData("")
 | 
			
		||||
        override val password: LiveData<String?> = MutableLiveData("")
 | 
			
		||||
        override val rememberCredential: LiveData<Boolean> = MutableLiveData(true)
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +38,8 @@ interface IAuthentication {
 | 
			
		|||
        override fun updateLoginField(login: String) = Unit
 | 
			
		||||
        override fun updatePasswordField(password: String) = Unit
 | 
			
		||||
        override fun updateRememberCredential(rememberCredential: Boolean) = Unit
 | 
			
		||||
        override fun clearState() = Unit
 | 
			
		||||
 | 
			
		||||
        override fun register() = Unit
 | 
			
		||||
        override fun login() = Unit
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,8 @@
 | 
			
		|||
 | 
			
		||||
    <string name="loading">Entering the Imperial Library of Trantor.</string>
 | 
			
		||||
 | 
			
		||||
    <string name="error_generic">Oops!</string>
 | 
			
		||||
 | 
			
		||||
    <string name="welcome_sign_in">Sign in to BibLib</string>
 | 
			
		||||
 | 
			
		||||
    <string name="authentication_login">Login</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue