Use bookRepository at application start.
This commit is contained in:
parent
b94eaba31b
commit
45f5e9023e
9 changed files with 256 additions and 145 deletions
|
|
@ -14,20 +14,11 @@ class APICacheRepository : IAPICacheRepository {
|
||||||
get() = preferences.new?.let { gson.fromJson(it, BookListResponse::class.java) }
|
get() = preferences.new?.let { gson.fromJson(it, BookListResponse::class.java) }
|
||||||
set(value) = gson.toJson(value).let { preferences.new = it }
|
set(value) = gson.toJson(value).let { preferences.new = it }
|
||||||
|
|
||||||
override var list: BookListResponse?
|
|
||||||
get() = preferences.list?.let { gson.fromJson(it, BookListResponse::class.java) }
|
|
||||||
set(value) = gson.toJson(value).let { preferences.list = it }
|
|
||||||
|
|
||||||
private var SharedPreferences.new: String?
|
private var SharedPreferences.new: String?
|
||||||
get() = getString(NEW, null)
|
get() = getString(NEW, null)
|
||||||
set(value) = edit { putString(NEW, value) }
|
set(value) = edit { putString(NEW, value) }
|
||||||
|
|
||||||
private var SharedPreferences.list: String?
|
|
||||||
get() = getString(LIST, null)
|
|
||||||
set(value) = edit { putString(LIST, value) }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NEW = "NEW"
|
const val NEW = "NEW"
|
||||||
const val LIST = "LIST"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,5 +4,4 @@ import com.pixelized.biblib.network.data.response.BookListResponse
|
||||||
|
|
||||||
interface IAPICacheRepository {
|
interface IAPICacheRepository {
|
||||||
var new: BookListResponse?
|
var new: BookListResponse?
|
||||||
var list: BookListResponse?
|
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ fun LoadingCard(
|
||||||
private fun LoadingCardLightPreview() {
|
private fun LoadingCardLightPreview() {
|
||||||
BibLibTheme(darkTheme = false) {
|
BibLibTheme(darkTheme = false) {
|
||||||
LoadingCard(
|
LoadingCard(
|
||||||
message = stringResource(id = R.string.loading)
|
message = stringResource(id = R.string.loading_authentication)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +62,7 @@ private fun LoadingCardLightPreview() {
|
||||||
private fun LoadingCardDarkPreview() {
|
private fun LoadingCardDarkPreview() {
|
||||||
BibLibTheme(darkTheme = true) {
|
BibLibTheme(darkTheme = true) {
|
||||||
LoadingCard(
|
LoadingCard(
|
||||||
message = stringResource(id = R.string.loading)
|
message = stringResource(id = R.string.loading_book)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +60,7 @@ fun SuccessCard(
|
||||||
@Composable
|
@Composable
|
||||||
private fun SuccessLightPreview() {
|
private fun SuccessLightPreview() {
|
||||||
BibLibTheme(darkTheme = false) {
|
BibLibTheme(darkTheme = false) {
|
||||||
SuccessCard(message = stringResource(id = R.string.authentication_success))
|
SuccessCard(message = stringResource(id = R.string.success_authentication))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +68,6 @@ private fun SuccessLightPreview() {
|
||||||
@Composable
|
@Composable
|
||||||
private fun SuccessDarkPreview() {
|
private fun SuccessDarkPreview() {
|
||||||
BibLibTheme(darkTheme = true) {
|
BibLibTheme(darkTheme = true) {
|
||||||
SuccessCard(message = stringResource(id = R.string.authentication_success))
|
SuccessCard(message = stringResource(id = R.string.success_authentication))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,15 +45,19 @@ import com.pixelized.biblib.ui.composable.items.dialog.SuccessCard
|
||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||||
import com.pixelized.biblib.ui.viewmodel.authentication.AuthenticationViewModel
|
import com.pixelized.biblib.ui.viewmodel.authentication.AuthenticationViewModel
|
||||||
import com.pixelized.biblib.ui.viewmodel.authentication.IAuthentication
|
import com.pixelized.biblib.ui.viewmodel.authentication.IAuthentication
|
||||||
|
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation
|
||||||
|
import com.pixelized.biblib.ui.viewmodel.initialisation.InitialisationViewModel
|
||||||
import com.pixelized.biblib.ui.viewmodel.navigation.INavigation
|
import com.pixelized.biblib.ui.viewmodel.navigation.INavigation
|
||||||
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
|
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
|
|
||||||
|
|
||||||
|
private const val LE_LOAD_BOOK = "LE_LOAD_BOOK"
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginScreenComposable(
|
fun LoginScreenComposable(
|
||||||
navigation: INavigation = viewModel<NavigationViewModel>(),
|
navigation: INavigation = viewModel<NavigationViewModel>(),
|
||||||
authentication: IAuthentication = viewModel<AuthenticationViewModel>(),
|
authentication: IAuthentication = viewModel<AuthenticationViewModel>(),
|
||||||
|
initialisation: IInitialisation = viewModel<InitialisationViewModel>()
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -61,10 +65,10 @@ fun LoginScreenComposable(
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
authentication.PrepareLoginWithGoogle()
|
authentication.PrepareLoginWithGoogle()
|
||||||
LoginScreenNavigationComposable(navigation, authentication)
|
LoginScreenNavigationComposable(navigation, authentication, initialisation)
|
||||||
|
|
||||||
LoginScreenContentComposable(authentication)
|
LoginScreenContentComposable(authentication)
|
||||||
LoginScreenDialogComposable(authentication)
|
LoginScreenDialogComposable(authentication, initialisation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,49 +95,87 @@ private fun LoginScreenContentComposable(
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun LoginScreenDialogComposable(
|
private fun LoginScreenDialogComposable(
|
||||||
authentication: IAuthentication
|
authentication: IAuthentication,
|
||||||
|
initialisation: IInitialisation,
|
||||||
) {
|
) {
|
||||||
val state = authentication.state.observeAsState()
|
val authenticationState = authentication.state.observeAsState()
|
||||||
|
val bookLoadingState = initialisation.state.observeAsState()
|
||||||
|
|
||||||
CrossFadeOverlay(
|
CrossFadeOverlay(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
if (state.value is IAuthentication.State.Error) {
|
if (authenticationState.value is IAuthentication.State.Error) {
|
||||||
authentication.clearState()
|
authentication.clearState()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visible = (state.value is IAuthentication.State.Initial).not()
|
visible = (authenticationState.value is IAuthentication.State.Initial
|
||||||
|
&& bookLoadingState.value is IInitialisation.State.Initial).not()
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
visible = state.value is IAuthentication.State.Error,
|
visible = authenticationState.value is IAuthentication.State.Error,
|
||||||
initiallyVisible = false,
|
initiallyVisible = false,
|
||||||
enter = expandVertically(Alignment.CenterVertically),
|
enter = expandVertically(Alignment.CenterVertically),
|
||||||
exit = shrinkVertically(Alignment.CenterVertically),
|
exit = shrinkVertically(Alignment.CenterVertically),
|
||||||
) {
|
) {
|
||||||
ErrorCard(
|
ErrorCard(
|
||||||
message = stringResource(id = R.string.error_generic),
|
message = stringResource(id = R.string.error_generic),
|
||||||
exception = (state.value as? IAuthentication.State.Error)?.exception
|
exception = (authenticationState.value as? IAuthentication.State.Error)?.exception
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
visible = state.value is IAuthentication.State.Loading,
|
visible = authenticationState.value is IAuthentication.State.Loading,
|
||||||
initiallyVisible = false,
|
initiallyVisible = false,
|
||||||
enter = expandVertically(Alignment.CenterVertically),
|
enter = expandVertically(Alignment.CenterVertically),
|
||||||
exit = shrinkVertically(Alignment.CenterVertically),
|
exit = shrinkVertically(Alignment.CenterVertically),
|
||||||
) {
|
) {
|
||||||
LoadingCard(
|
LoadingCard(
|
||||||
message = stringResource(id = R.string.loading)
|
message = stringResource(id = R.string.loading_authentication)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
visible = state.value is IAuthentication.State.Connect,
|
visible = authenticationState.value is IAuthentication.State.Connect,
|
||||||
initiallyVisible = false,
|
initiallyVisible = false,
|
||||||
enter = expandVertically(Alignment.CenterVertically),
|
enter = expandVertically(Alignment.CenterVertically),
|
||||||
exit = shrinkVertically(Alignment.CenterVertically),
|
exit = shrinkVertically(Alignment.CenterVertically),
|
||||||
) {
|
) {
|
||||||
SuccessCard(
|
SuccessCard(
|
||||||
message = stringResource(id = R.string.authentication_success)
|
message = stringResource(id = R.string.success_authentication)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
visible = bookLoadingState.value is IInitialisation.State.Error,
|
||||||
|
initiallyVisible = false,
|
||||||
|
enter = expandVertically(Alignment.CenterVertically),
|
||||||
|
exit = shrinkVertically(Alignment.CenterVertically),
|
||||||
|
) {
|
||||||
|
ErrorCard(
|
||||||
|
message = stringResource(id = R.string.error_generic),
|
||||||
|
exception = (bookLoadingState.value as? IInitialisation.State.Error)?.exception
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
visible = bookLoadingState.value is IInitialisation.State.Loading,
|
||||||
|
initiallyVisible = false,
|
||||||
|
enter = expandVertically(Alignment.CenterVertically),
|
||||||
|
exit = shrinkVertically(Alignment.CenterVertically),
|
||||||
|
) {
|
||||||
|
LoadingCard(
|
||||||
|
message = stringResource(id = R.string.loading_book)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
visible = bookLoadingState.value is IInitialisation.State.Finished,
|
||||||
|
initiallyVisible = false,
|
||||||
|
enter = expandVertically(Alignment.CenterVertically),
|
||||||
|
exit = shrinkVertically(Alignment.CenterVertically),
|
||||||
|
) {
|
||||||
|
SuccessCard(
|
||||||
|
message = stringResource(id = R.string.success_book)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -142,15 +184,21 @@ private fun LoginScreenDialogComposable(
|
||||||
@Composable
|
@Composable
|
||||||
private fun LoginScreenNavigationComposable(
|
private fun LoginScreenNavigationComposable(
|
||||||
navigation: INavigation,
|
navigation: INavigation,
|
||||||
authentication: IAuthentication
|
authentication: IAuthentication,
|
||||||
|
initialisation: IInitialisation,
|
||||||
) {
|
) {
|
||||||
val state = authentication.state.observeAsState()
|
val authenticationState = authentication.state.observeAsState()
|
||||||
if (state.value == IAuthentication.State.Connect) {
|
val bookLoadingState = initialisation.state.observeAsState()
|
||||||
LaunchedEffect(key1 = "navigateTo(MainScreen)") {
|
|
||||||
delay(1000)
|
if (authenticationState.value == IAuthentication.State.Connect) {
|
||||||
navigation.navigateTo(INavigation.Screen.MainScreen)
|
LaunchedEffect(LE_LOAD_BOOK) {
|
||||||
|
initialisation.loadBook()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bookLoadingState.value is IInitialisation.State.Finished) {
|
||||||
|
navigation.navigateTo(INavigation.Screen.MainScreen)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -317,6 +365,7 @@ fun LoginScreenComposablePreview() {
|
||||||
BibLibTheme {
|
BibLibTheme {
|
||||||
val navigationViewModel = INavigation.Mock()
|
val navigationViewModel = INavigation.Mock()
|
||||||
val authenticationViewModel = IAuthentication.Mock()
|
val authenticationViewModel = IAuthentication.Mock()
|
||||||
LoginScreenComposable(navigationViewModel, authenticationViewModel)
|
val initialisation = IInitialisation.Mock()
|
||||||
|
LoginScreenComposable(navigationViewModel, authenticationViewModel, initialisation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,14 @@ package com.pixelized.biblib.ui.composable.screen
|
||||||
|
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
@ -15,6 +18,8 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.pixelized.biblib.BuildConfig
|
import com.pixelized.biblib.BuildConfig
|
||||||
import com.pixelized.biblib.R
|
import com.pixelized.biblib.R
|
||||||
|
import com.pixelized.biblib.ui.composable.items.dialog.CrossFadeOverlay
|
||||||
|
import com.pixelized.biblib.ui.composable.items.dialog.ErrorCard
|
||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||||
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation
|
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation
|
||||||
import com.pixelized.biblib.ui.viewmodel.initialisation.InitialisationViewModel
|
import com.pixelized.biblib.ui.viewmodel.initialisation.InitialisationViewModel
|
||||||
|
|
@ -23,12 +28,16 @@ import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
private const val ANIMATION_DURATION = 1000
|
||||||
|
private const val LAUNCH_EFFECT_LOAD_APPLICATION = "LoadApplication"
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SplashScreenComposablePreview() {
|
fun SplashScreenComposablePreview() {
|
||||||
BibLibTheme {
|
BibLibTheme {
|
||||||
SplashScreenComposable(IInitialisation.Mock(), INavigation.Mock())
|
val initialisation = IInitialisation.Mock(IInitialisation.State.Loading)
|
||||||
|
val navigation = INavigation.Mock()
|
||||||
|
SplashScreenComposable(initialisation, navigation, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,92 +45,138 @@ fun SplashScreenComposablePreview() {
|
||||||
@Composable
|
@Composable
|
||||||
fun SplashScreenComposable(
|
fun SplashScreenComposable(
|
||||||
initialisation: IInitialisation = viewModel<InitialisationViewModel>(),
|
initialisation: IInitialisation = viewModel<InitialisationViewModel>(),
|
||||||
navigation: INavigation = viewModel<NavigationViewModel>()
|
navigation: INavigation = viewModel<NavigationViewModel>(),
|
||||||
|
initiallyVisible: Boolean = false,
|
||||||
|
) {
|
||||||
|
val state by initialisation.state.observeAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(LAUNCH_EFFECT_LOAD_APPLICATION) {
|
||||||
|
initialisation.loadApplication()
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentComposable(state = state, initiallyVisible = initiallyVisible)
|
||||||
|
DialogComposable(state = state)
|
||||||
|
NavigationComposable(navigation = navigation, state = state)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun ContentComposable(
|
||||||
|
state: IInitialisation.State?,
|
||||||
|
duration: Int = ANIMATION_DURATION,
|
||||||
|
initiallyVisible: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val duration = 1000
|
|
||||||
val typography = MaterialTheme.typography
|
val typography = MaterialTheme.typography
|
||||||
initialisation.LoadApplication {
|
val visible = state !is IInitialisation.State.Finished
|
||||||
val visible = it !is IInitialisation.State.Finished
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.width(240.dp)
|
||||||
.fillMaxWidth()
|
.align(Alignment.Center)
|
||||||
.padding(16.dp),
|
|
||||||
) {
|
) {
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.width(240.dp)
|
|
||||||
.align(Alignment.Center)
|
|
||||||
) {
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = visible,
|
|
||||||
initiallyVisible = false,
|
|
||||||
enter = fadeIn(animationSpec = tween(duration))
|
|
||||||
+ slideInVertically(
|
|
||||||
initialOffsetY = { height -> -height },
|
|
||||||
animationSpec = tween(duration)
|
|
||||||
),
|
|
||||||
exit = fadeOut(animationSpec = tween(duration))
|
|
||||||
+ slideOutVertically(
|
|
||||||
targetOffsetY = { height -> -height },
|
|
||||||
animationSpec = tween(duration)
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
style = typography.h4,
|
|
||||||
text = "Welcome to"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
AnimatedVisibility(
|
|
||||||
modifier = Modifier.align(Alignment.End),
|
|
||||||
visible = visible,
|
|
||||||
initiallyVisible = false,
|
|
||||||
enter = fadeIn(animationSpec = tween(duration))
|
|
||||||
+ slideInVertically(
|
|
||||||
initialOffsetY = { height -> height },
|
|
||||||
animationSpec = tween(duration)
|
|
||||||
),
|
|
||||||
exit = fadeOut(animationSpec = tween(duration))
|
|
||||||
+ slideOutVertically(
|
|
||||||
targetOffsetY = { height -> height },
|
|
||||||
animationSpec = tween(duration)
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
style = typography.h4,
|
|
||||||
text = stringResource(id = R.string.app_name)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
modifier = Modifier.align(Alignment.BottomEnd),
|
|
||||||
visible = visible,
|
visible = visible,
|
||||||
enter = fadeIn(animationSpec = tween(duration)),
|
initiallyVisible = initiallyVisible,
|
||||||
exit = fadeOut(animationSpec = tween(duration)),
|
enter = fadeIn(animationSpec = tween(duration))
|
||||||
|
+ slideInVertically(
|
||||||
|
initialOffsetY = { height -> -height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
|
exit = fadeOut(animationSpec = tween(duration))
|
||||||
|
+ slideOutVertically(
|
||||||
|
targetOffsetY = { height -> -height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = typography.caption,
|
style = typography.h4,
|
||||||
text = stringResource(
|
text = "Welcome to"
|
||||||
R.string.app_version,
|
)
|
||||||
BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()),
|
}
|
||||||
BuildConfig.VERSION_NAME,
|
AnimatedVisibility(
|
||||||
BuildConfig.VERSION_CODE
|
modifier = Modifier.align(Alignment.End),
|
||||||
)
|
visible = visible,
|
||||||
|
initiallyVisible = initiallyVisible,
|
||||||
|
enter = fadeIn(animationSpec = tween(duration))
|
||||||
|
+ slideInVertically(
|
||||||
|
initialOffsetY = { height -> height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
|
exit = fadeOut(animationSpec = tween(duration))
|
||||||
|
+ slideOutVertically(
|
||||||
|
targetOffsetY = { height -> height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = typography.h4,
|
||||||
|
text = stringResource(id = R.string.app_name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it is IInitialisation.State.Finished) {
|
AnimatedVisibility(
|
||||||
LaunchedEffect(key1 = "SplashScreen.navigateTo()") {
|
modifier = Modifier.align(Alignment.BottomEnd),
|
||||||
delay(1000)
|
visible = visible,
|
||||||
if (it.needLogin) {
|
initiallyVisible = initiallyVisible,
|
||||||
navigation.navigateTo(INavigation.Screen.LoginScreen)
|
enter = fadeIn(animationSpec = tween(duration)),
|
||||||
} else {
|
exit = fadeOut(animationSpec = tween(duration)),
|
||||||
navigation.navigateTo(INavigation.Screen.MainScreen)
|
) {
|
||||||
}
|
Text(
|
||||||
}
|
style = typography.caption,
|
||||||
|
text = stringResource(
|
||||||
|
R.string.app_version,
|
||||||
|
BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()),
|
||||||
|
BuildConfig.VERSION_NAME,
|
||||||
|
BuildConfig.VERSION_CODE
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun DialogComposable(state: IInitialisation.State?) {
|
||||||
|
CrossFadeOverlay(
|
||||||
|
modifier = Modifier.clickable {},
|
||||||
|
visible = state is IInitialisation.State.Error
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
visible = true,
|
||||||
|
initiallyVisible = false,
|
||||||
|
enter = expandVertically(Alignment.CenterVertically),
|
||||||
|
exit = shrinkVertically(Alignment.CenterVertically),
|
||||||
|
) {
|
||||||
|
ErrorCard(
|
||||||
|
message = stringResource(id = R.string.error_generic),
|
||||||
|
exception = (state as? IInitialisation.State.Error)?.exception
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun NavigationComposable(
|
||||||
|
navigation: INavigation,
|
||||||
|
state: IInitialisation.State?,
|
||||||
|
duration: Int = ANIMATION_DURATION,
|
||||||
|
) {
|
||||||
|
if (state is IInitialisation.State.Finished) {
|
||||||
|
LaunchedEffect(key1 = "SplashScreen.navigateTo()") {
|
||||||
|
delay(duration.toLong())
|
||||||
|
if (state.needLogin) {
|
||||||
|
navigation.navigateTo(INavigation.Screen.LoginScreen)
|
||||||
|
} else {
|
||||||
|
navigation.navigateTo(INavigation.Screen.MainScreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,25 +1,35 @@
|
||||||
package com.pixelized.biblib.ui.viewmodel.initialisation
|
package com.pixelized.biblib.ui.viewmodel.initialisation
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
|
||||||
interface IInitialisation {
|
interface IInitialisation {
|
||||||
|
|
||||||
@Composable
|
val state: LiveData<State>
|
||||||
fun LoadApplication(content: @Composable (State) -> Unit)
|
|
||||||
|
|
||||||
@Composable
|
fun loadApplication()
|
||||||
fun LoadBook(content: @Composable (State) -> Unit)
|
|
||||||
|
|
||||||
|
fun loadBook()
|
||||||
|
|
||||||
|
@Stable
|
||||||
sealed class State {
|
sealed class State {
|
||||||
|
@Stable
|
||||||
object Initial : State()
|
object Initial : State()
|
||||||
|
|
||||||
|
@Stable
|
||||||
object Loading : State()
|
object Loading : State()
|
||||||
|
|
||||||
|
@Stable
|
||||||
class Finished(val needLogin: Boolean) : State()
|
class Finished(val needLogin: Boolean) : State()
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class Error(val exception: Exception) : State()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Mock(private val state: State = State.Loading) : IInitialisation {
|
class Mock(private val value: State = State.Initial) : IInitialisation {
|
||||||
@Composable
|
override val state get() = MutableLiveData(value)
|
||||||
override fun LoadApplication(content: (State) -> Unit) = content(state)
|
override fun loadApplication() = Unit
|
||||||
@Composable
|
override fun loadBook() = Unit
|
||||||
override fun LoadBook(content: (State) -> Unit) = content(state)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.pixelized.biblib.ui.viewmodel.initialisation
|
package com.pixelized.biblib.ui.viewmodel.initialisation
|
||||||
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.biblib.network.client.IBibLibClient
|
import com.pixelized.biblib.network.client.IBibLibClient
|
||||||
import com.pixelized.biblib.network.data.query.AuthLoginQuery
|
import com.pixelized.biblib.network.data.query.AuthLoginQuery
|
||||||
import com.pixelized.biblib.network.factory.BookFactory
|
import com.pixelized.biblib.network.factory.BookFactory
|
||||||
|
|
@ -11,7 +13,10 @@ import com.pixelized.biblib.repository.credential.ICredentialRepository
|
||||||
import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository
|
import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository
|
||||||
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation.State.*
|
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation.State.*
|
||||||
import com.pixelized.biblib.utils.injection.inject
|
import com.pixelized.biblib.utils.injection.inject
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
class InitialisationViewModel : ViewModel(), IInitialisation {
|
class InitialisationViewModel : ViewModel(), IInitialisation {
|
||||||
private val credentialRepository: ICredentialRepository by inject()
|
private val credentialRepository: ICredentialRepository by inject()
|
||||||
|
|
@ -20,33 +25,37 @@ class InitialisationViewModel : ViewModel(), IInitialisation {
|
||||||
private val client: IBibLibClient by inject()
|
private val client: IBibLibClient by inject()
|
||||||
private val apiCache: IAPICacheRepository by inject()
|
private val apiCache: IAPICacheRepository by inject()
|
||||||
|
|
||||||
@Composable
|
private val _state = MutableLiveData<IInitialisation.State>()
|
||||||
override fun LoadApplication(content: @Composable (IInitialisation.State) -> Unit) {
|
override val state: LiveData<IInitialisation.State> get() = _state
|
||||||
val state: MutableState<IInitialisation.State> = remember { mutableStateOf(Initial) }
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = "LoadApplication") {
|
override fun loadApplication() {
|
||||||
state.value = Loading
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_state.postValue(Initial)
|
||||||
|
_state.postValue(Loading)
|
||||||
delay(2000)
|
delay(2000)
|
||||||
|
try {
|
||||||
val loggedIn = loginWithGoogle() || loginWithCredential()
|
val loggedIn = loginWithGoogle() || loginWithCredential()
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
loadNewBooks() && loadAllBooks()
|
loadNewBooks() && loadAllBooks()
|
||||||
|
}
|
||||||
|
_state.postValue(Finished(needLogin = loggedIn.not()))
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
_state.postValue(Error(exception))
|
||||||
}
|
}
|
||||||
state.value = Finished(needLogin = loggedIn.not())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
content(state.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
override fun loadBook() {
|
||||||
override fun LoadBook(content: (IInitialisation.State) -> Unit) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val state: MutableState<IInitialisation.State> = remember { mutableStateOf(Initial) }
|
_state.postValue(Initial)
|
||||||
LaunchedEffect(key1 = "LoadBook") {
|
_state.postValue(Loading)
|
||||||
state.value = Loading
|
try {
|
||||||
loadNewBooks() && loadAllBooks()
|
loadNewBooks() && loadAllBooks()
|
||||||
state.value = Finished(needLogin = false)
|
_state.postValue(Finished(needLogin = false))
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
_state.postValue(Error(exception))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
content(state.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loginWithGoogle(): Boolean {
|
private suspend fun loginWithGoogle(): Boolean {
|
||||||
|
|
@ -104,9 +113,6 @@ class InitialisationViewModel : ViewModel(), IInitialisation {
|
||||||
|
|
||||||
private suspend fun loadAllBooks(): Boolean {
|
private suspend fun loadAllBooks(): Boolean {
|
||||||
client.service.list().let { response ->
|
client.service.list().let { response ->
|
||||||
// TODO: useless isn't it ?
|
|
||||||
apiCache.list = response
|
|
||||||
|
|
||||||
val factory = BookFactory()
|
val factory = BookFactory()
|
||||||
val books = response.data?.map { dto -> factory.fromListResponseToBook(dto, false) }
|
val books = response.data?.map { dto -> factory.fromListResponseToBook(dto, false) }
|
||||||
books?.let { data -> bookRepository.update(data) }
|
books?.let { data -> bookRepository.update(data) }
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,17 @@
|
||||||
<string name="action_send">SEND</string>
|
<string name="action_send">SEND</string>
|
||||||
<string name="action_google_sign_in">Sign in with Google</string>
|
<string name="action_google_sign_in">Sign in with Google</string>
|
||||||
|
|
||||||
<string name="loading">Entering the Imperial Library of Trantor.</string>
|
|
||||||
|
|
||||||
<string name="error_generic">Oops!</string>
|
<string name="error_generic">Oops!</string>
|
||||||
|
<string name="loading_authentication">Entering the Imperial Library of Trantor.</string>
|
||||||
|
<string name="loading_book">Entering the Imperial Library of Trantor.</string>
|
||||||
|
<string name="success_authentication">Authentication successful</string>
|
||||||
|
<string name="success_book">Library successfully loaded</string>
|
||||||
|
|
||||||
<string name="welcome_sign_in">Sign in to BibLib</string>
|
<string name="welcome_sign_in">Sign in to BibLib</string>
|
||||||
|
|
||||||
<string name="authentication_login">Login</string>
|
<string name="authentication_login">Login</string>
|
||||||
<string name="authentication_password">Password</string>
|
<string name="authentication_password">Password</string>
|
||||||
<string name="authentication_credential_remember">Remember my credential</string>
|
<string name="authentication_credential_remember">Remember my credential</string>
|
||||||
<string name="authentication_success">Authentication successful</string>
|
|
||||||
|
|
||||||
<string name="detail_rating">Rating</string>
|
<string name="detail_rating">Rating</string>
|
||||||
<string name="detail_language">Language</string>
|
<string name="detail_language">Language</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue