Profile refresh mechanism when trying to send a book with no associated emails.

This commit is contained in:
Thomas Andres Gomez 2022-10-26 10:19:46 +02:00
parent 29a427ac5a
commit 80bcb9a686
11 changed files with 117 additions and 60 deletions

View file

@ -1,5 +1,3 @@
# TODO
* Profile refresh.
* especially when no mail available.
* Profile dialog close button.

View file

@ -1,8 +1,9 @@
# 0.1.4
> Not yet
* Fix SignIn and EditProfile Url.
* Fix SignIn and EditProfile Urls.
* Fix Search IME padding.
* Clear search filter after validation.
* Clear search filter after validation.
* Profile refresh mechanism when trying to send a book with no associated emails.
# 0.1.3
> Published 25 October 2022

View file

@ -12,6 +12,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.pixelized.biblib.ui.navigation.ScreenNavHost
import com.pixelized.biblib.ui.screen.launch.LauncherViewModel
import com.pixelized.biblib.utils.extention.bibLib
import com.skydoves.landscapist.glide.LocalGlideRequestOptions
val LocalSnackHostState = staticCompositionLocalOf<SnackbarHostState> {
@ -40,6 +41,7 @@ fun MainContent(
) { snackBarData ->
Snackbar(
snackbarData = snackBarData,
shape = MaterialTheme.shapes.medium,
backgroundColor = MaterialTheme.colors.error,
contentColor = MaterialTheme.colors.onError,
actionColor = MaterialTheme.colors.onError,

View file

@ -1,23 +1,15 @@
package com.pixelized.biblib.ui.screen.home.detail
import android.util.Log
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.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.network.factory.UserFactory
import com.pixelized.biblib.utils.extention.capitalize
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class BookOptionViewModel @Inject constructor(
private val client: IBibLibClient,
) : ViewModel() {
class BookOptionViewModel @Inject constructor() : ViewModel() {
var emails by mutableStateOf(listOf<OptionUio>())
private set
@ -25,19 +17,9 @@ class BookOptionViewModel @Inject constructor(
var formats by mutableStateOf(initialFormat())
private set
init {
viewModelScope.launch(Dispatchers.IO) {
try {
val factory = UserFactory()
val response = client.service.user()
val data = factory.fromUserResponseToUser(response)
emails = data.amazonEmails.mapIndexed { index, mail ->
OptionUio(value = mail, selected = index == 0)
}
} catch (exception: Exception) {
Log.e("AccountViewModel", exception.message, exception)
}
fun updateEmails(amazonEmails: List<String>) {
emails = amazonEmails.mapIndexed { index, mail ->
OptionUio(value = mail, selected = index == 0)
}
}

View file

@ -1,8 +1,8 @@
package com.pixelized.biblib.ui.screen.home.detail
import android.content.Context
import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@ -20,6 +20,7 @@ import com.pixelized.biblib.ui.screen.home.page.profile.ProfileViewModel
import com.pixelized.biblib.ui.screen.home.page.profile.UserUio
import com.pixelized.biblib.ui.theme.color.ShadowPalette
import com.pixelized.biblib.utils.extention.bibLib
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
@Stable
@ -77,6 +78,7 @@ fun DetailScreen(
modifier = Modifier
.navigationBarsPadding()
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
emails = profileViewModel.mails,
onHelp = { uri ->
uriHandler.openUri(uri)
},
@ -96,17 +98,13 @@ fun DetailScreen(
.systemBarsPadding(),
book = detail,
onSend = {
scope.launch {
val user = profileViewModel.user as? StateUio.Success<UserUio>
if (user?.value?.amazonEmails?.isEmpty() == true) {
snackBarHost.showSnackbar(
message = context.getString(R.string.error_no_amazon_email),
duration = SnackbarDuration.Long,
)
} else {
emailSheetState.show()
}
}
onSend(
context = context,
profileViewModel = profileViewModel,
snackBarHost = snackBarHost,
emailSheetState = emailSheetState,
userState = profileViewModel.user
)
}
)
DetailScreenSendLoader(
@ -159,6 +157,35 @@ fun DetailScreen(
}
}
@OptIn(ExperimentalMaterialApi::class)
private suspend fun onSend(
context: Context,
profileViewModel: ProfileViewModel,
snackBarHost: SnackbarHostState,
emailSheetState: ModalBottomSheetState,
userState: StateUio<UserUio>,
) {
val user = userState as? StateUio.Success<UserUio>
if (user?.value?.amazonEmails?.isEmpty() == true) {
val result = snackBarHost.showSnackbar(
message = context.getString(R.string.error_send_no_amazon_email_message),
actionLabel = context.getString(R.string.error_send_no_amazon_email_action),
duration = SnackbarDuration.Indefinite,
)
if (result == SnackbarResult.ActionPerformed) {
onSend(
context = context,
profileViewModel = profileViewModel,
snackBarHost = snackBarHost,
emailSheetState = emailSheetState,
userState = profileViewModel.updateUser()
)
}
} else {
emailSheetState.show()
}
}
@Composable
private fun EmptyDetail(
modifier: Modifier = Modifier,

View file

@ -15,6 +15,7 @@ import androidx.compose.material.icons.filled.Send
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
@ -37,6 +38,7 @@ import com.pixelized.biblib.utils.extention.bibLib
import com.pixelized.biblib.utils.extention.default
import com.skydoves.landscapist.CircularReveal
import com.skydoves.landscapist.glide.GlideImage
import kotlinx.coroutines.launch
import java.io.Serializable
@Stable
@ -75,8 +77,10 @@ fun DetailScreenContent(
book: BookDetailUio,
onMobi: () -> Unit = default(),
onEpub: () -> Unit = default(),
onSend: () -> Unit = default(),
onSend: suspend () -> Unit = { },
) {
val scope = rememberCoroutineScope()
AnimatedDelayer(
targetState = book.id,
) {
@ -130,7 +134,7 @@ fun DetailScreenContent(
) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = onSend,
onClick = { scope.launch { onSend() } },
) {
Icon(imageVector = Icons.Default.Send, contentDescription = "")
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))

View file

@ -15,6 +15,7 @@ import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -26,6 +27,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.screen.home.page.profile.UserUio
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.*
@ -49,9 +51,13 @@ annotation class Format {
fun DetailScreenSendOption(
modifier: Modifier = Modifier,
optionViewModel: BookOptionViewModel = hiltViewModel(),
emails: List<String>,
onHelp: (url: String) -> Unit = default<String>(),
onSend: (email: String, format: String) -> Unit = { _, _ -> },
) {
LaunchedEffect(key1 = emails) {
optionViewModel.updateEmails(amazonEmails = emails)
}
DetailScreenSendOption(
modifier = modifier,
emails = optionViewModel.emails,
@ -138,7 +144,7 @@ fun DetailScreenSendOption(
.padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
color = MaterialTheme.bibLib.colors.typography.easy,
style = MaterialTheme.typography.caption,
text = stringResource(id = R.string.detail_option_mail),
text = stringResource(id = R.string.detail_option_format),
)
}
items(items = formats) { format ->

View file

@ -5,6 +5,8 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.net.Uri
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
@ -12,8 +14,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.R
import com.pixelized.biblib.network.client.IBibLibClient
import com.pixelized.biblib.ui.composable.StateUio
import com.pixelized.biblib.ui.navigation.LocalScreenNavHostController
@ -62,8 +66,9 @@ private fun ProfileScreenContent(
Text(
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
text = "Welcome"
text = stringResource(id = R.string.profile_title)
)
Text(
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.primary,
@ -94,14 +99,28 @@ private fun ProfileScreenContent(
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.dp16),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
text = "Linked emails:"
text = stringResource(id = R.string.profile_emails)
)
user.amazonEmails.forEach {
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = it,
)
LazyColumn {
if (user.amazonEmails.isEmpty()) {
item {
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.error,
text = stringResource(id = R.string.profile_emails_empty),
)
}
} else {
items(
items = user.amazonEmails
) {
Text(
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
text = it,
)
}
}
}
Button(
@ -117,19 +136,18 @@ private fun ProfileScreenContent(
)
Text(
modifier = Modifier.padding(start = MaterialTheme.bibLib.dimen.dp8),
text = "Edit profile"
text = stringResource(id = R.string.profile_edit_action)
)
}
Button(
modifier = Modifier
.align(Alignment.End),
colors = ButtonDefaults.outlinedButtonColors(),
onClick = onLogoutClick,
) {
Text(
text = "Logout"
text = stringResource(id = R.string.profile_logout_action)
)
}
}

View file

@ -1,6 +1,7 @@
package com.pixelized.biblib.ui.screen.home.page.profile
import android.util.Log
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@ -15,6 +16,7 @@ import com.pixelized.biblib.ui.composable.StateUio
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@HiltViewModel
@ -27,13 +29,19 @@ class ProfileViewModel @Inject constructor(
var user by mutableStateOf<StateUio<UserUio>>(StateUio.Progress())
private set
init {
updateUser()
val mails: List<String> by derivedStateOf {
(user as? StateUio.Success<UserUio>)?.value?.amazonEmails ?: emptyList()
}
private fun updateUser() {
init {
viewModelScope.launch(Dispatchers.IO) {
user = try {
updateUser()
}
}
suspend fun updateUser(): StateUio<UserUio> {
return withContext(Dispatchers.IO) {
try {
val factory = UserFactory()
val response = client.service.user()
val data = factory.fromUserResponseToUser(response)
@ -41,6 +49,8 @@ class ProfileViewModel @Inject constructor(
} catch (exception: Exception) {
Log.e("AccountViewModel", exception.message, exception)
StateUio.Failure(exception)
}.also {
user = it
}
}
}

View file

@ -21,6 +21,7 @@
<string name="error_book_update_message">La mise à jour de la librarie à échouée.</string>
<string name="error_get_book_detail_message">La récupération des détails a échouée.</string>
<string name="error_send_book_message">L\'envoi de l\'eBook a échoué.</string>
<string name="error_send_no_amazon_email_message">Vous n\'avez aucun mail lié à votre compte. Merci de vous rendre sur https://bib.bibulle.fr/profile pour en ajouter un.</string>
<!-- Dialogs -->
@ -28,7 +29,6 @@
<string name="error_authentication">Oups, la connection a échoué !</string>
<string name="error_book">Oups! le téléchargement de la librairy à échoué !</string>
<string name="error_offline">Vous êtes hors ligne.</string>
<string name="error_no_amazon_email">Vous n\'avez aucun mail lié à votre compte. Merci de vous rendre sur https://bib.bibulle.fr/ et d\'éditez voter profile pour en ajouter un.</string>
<string name="loading_authentication">Ouverture de BibLibrary.</string>
<string name="loading_book">Téléchargement de BibLibrary.</string>
@ -70,6 +70,10 @@
<string name="search_sort_by">Trié par : %1$s</string>
<string name="profile_title">Profile</string>
<string name="profile_title">Bonjour</string>
<string name="profile_emails">Amails associés :</string>
<string name="profile_emails_empty">Aucun email associé</string>
<string name="profile_edit_action">Édition du profile</string>
<string name="profile_logout_action">Déconnexion</string>
</resources>

View file

@ -32,6 +32,8 @@
<string name="error_get_book_detail_action" translatable="false">@string/error_action_retry</string>
<string name="error_send_book_message">Failed to send the book to your kindle.</string>
<string name="error_send_book_action" translatable="false">@string/error_action_retry</string>
<string name="error_send_no_amazon_email_message">You have no email linked to your account. Please go to https://bib.bibulle.fr/profile to add one.</string>
<string name="error_send_no_amazon_email_action" translatable="false">@string/error_action_retry</string>
<!-- Dialogs -->
@ -39,7 +41,6 @@
<string name="error_authentication">Oops, connection failed!</string>
<string name="error_book">Oops! library download failed!</string>
<string name="error_offline">You are offline.</string>
<string name="error_no_amazon_email">You have no email linked to your account. Please go to https://bib.bibulle.fr/ and edit your profile to add one.</string>
<string name="loading_authentication">Opening the BibLibrary.</string>
<string name="loading_book">Downloading the BibLibrary.</string>
@ -82,6 +83,10 @@
<string name="search_sort_by">Sort by: %1$s</string>
<string name="profile_title">Profil</string>
<string name="profile_title">Welcome</string>
<string name="profile_emails">Linked emails:</string>
<string name="profile_emails_empty">no associated email</string>
<string name="profile_edit_action">Edit profile</string>
<string name="profile_logout_action">Logout</string>
</resources>