diff --git a/TODO.md b/TODO.md index 26d1c05..4776142 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,3 @@ # TODO -* Profile refresh. - * especially when no mail available. * Profile dialog close button. \ No newline at end of file diff --git a/app/CHANGELOG.md b/app/CHANGELOG.md index ba95d8c..e524de7 100644 --- a/app/CHANGELOG.md +++ b/app/CHANGELOG.md @@ -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 diff --git a/app/src/main/java/com/pixelized/biblib/ui/MainContent.kt b/app/src/main/java/com/pixelized/biblib/ui/MainContent.kt index 17efc3c..7c499ed 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/MainContent.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/MainContent.kt @@ -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 { @@ -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, diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookOptionViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookOptionViewModel.kt index f7a74fb..35265e9 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookOptionViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookOptionViewModel.kt @@ -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()) 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) { + emails = amazonEmails.mapIndexed { index, mail -> + OptionUio(value = mail, selected = index == 0) } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt index e805966..a37e253 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt @@ -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 - 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, +) { + val user = userState as? StateUio.Success + 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, diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt index 8b9e9d7..2fc22e1 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenContent.kt @@ -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)) diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenSendOption.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenSendOption.kt index 5e3fcd1..a2425f6 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenSendOption.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenSendOption.kt @@ -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, onHelp: (url: String) -> Unit = default(), 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 -> diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt index 07fe3b0..2e1c0a8 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt @@ -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) ) } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfileViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfileViewModel.kt index cfbc967..f81fa9a 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfileViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfileViewModel.kt @@ -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.Progress()) private set - init { - updateUser() + val mails: List by derivedStateOf { + (user as? StateUio.Success)?.value?.amazonEmails ?: emptyList() } - private fun updateUser() { + init { viewModelScope.launch(Dispatchers.IO) { - user = try { + updateUser() + } + } + + suspend fun updateUser(): StateUio { + 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 } } } diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index b524672..4e9608d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -21,6 +21,7 @@ La mise à jour de la librarie à échouée. La récupération des détails a échouée. L\'envoi de l\'eBook a échoué. + Vous n\'avez aucun mail lié à votre compte. Merci de vous rendre sur https://bib.bibulle.fr/profile pour en ajouter un. @@ -28,7 +29,6 @@ Oups, la connection a échoué ! Oups! le téléchargement de la librairy à échoué ! Vous êtes hors ligne. - 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. Ouverture de BibLibrary. Téléchargement de BibLibrary. @@ -70,6 +70,10 @@ Trié par : %1$s - Profile + Bonjour + Amails associés : + Aucun email associé + Édition du profile + Déconnexion \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08f1889..25c0b4c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,6 +32,8 @@ @string/error_action_retry Failed to send the book to your kindle. @string/error_action_retry + You have no email linked to your account. Please go to https://bib.bibulle.fr/profile to add one. + @string/error_action_retry @@ -39,7 +41,6 @@ Oops, connection failed! Oops! library download failed! You are offline. - You have no email linked to your account. Please go to https://bib.bibulle.fr/ and edit your profile to add one. Opening the BibLibrary. Downloading the BibLibrary. @@ -82,6 +83,10 @@ Sort by: %1$s - Profil + Welcome + Linked emails: + no associated email + Edit profile + Logout \ No newline at end of file