From a86163360dd53619701203555ef0fae37c1fc445 Mon Sep 17 00:00:00 2001 From: acclorite Date: Thu, 8 Aug 2024 14:43:38 +0300 Subject: [PATCH] AboutScreen: Added loading indicator to "Check For Updates". --- .../presentation/screens/about/AboutScreen.kt | 47 +++++++++------ .../screens/about/components/AboutItem.kt | 60 ++++++++++++++----- .../screens/about/data/AboutState.kt | 2 + .../screens/about/data/AboutViewModel.kt | 17 ++++++ .../book_story/presentation/ui/Transitions.kt | 24 ++++++++ 5 files changed, 116 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/AboutScreen.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/AboutScreen.kt index d7ea1bc5..69426805 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/AboutScreen.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/AboutScreen.kt @@ -144,27 +144,36 @@ private fun AboutScreen( ) append("\n") append(stringResource(id = R.string.app_version_option_desc_2)) - } + }, + showLoading = state.value.updateLoading ) { - onEvent( - AboutEvent.OnCheckForUpdates( - context = context, - noUpdatesFound = { - Toast.makeText( - context, - context.getString(R.string.no_updates), - Toast.LENGTH_LONG - ).show() - }, - error = { - Toast.makeText( - context, - context.getString(R.string.error_check_internet), - Toast.LENGTH_LONG - ).show() - } + if (!state.value.alreadyCheckedForUpdates) { + onEvent( + AboutEvent.OnCheckForUpdates( + context = context, + noUpdatesFound = { + Toast.makeText( + context, + context.getString(R.string.no_updates), + Toast.LENGTH_LONG + ).show() + }, + error = { + Toast.makeText( + context, + context.getString(R.string.error_check_internet), + Toast.LENGTH_LONG + ).show() + } + ) ) - ) + } else { + Toast.makeText( + context, + context.getString(R.string.no_updates), + Toast.LENGTH_LONG + ).show() + } } } diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/components/AboutItem.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/components/AboutItem.kt index b8a7af7e..3628ca91 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/components/AboutItem.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/components/AboutItem.kt @@ -1,22 +1,38 @@ package ua.acclorite.book_story.presentation.screens.about.components import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace /** - * About screen item. + * About Screen Item. + * Clickable item, that has title, description and loading state. + * + * @param modifier [Modifier]. + * @param title Title of the item. + * @param description Description of the item. + * @param verticalPadding Vertical item padding. + * @param showLoading Whether loading indicator is shown. + * @param isOnClickEnabled Whether this item is clickable. + * @param onClick OnClick callback. */ @Composable fun AboutItem( @@ -24,31 +40,45 @@ fun AboutItem( title: String, description: AnnotatedString?, verticalPadding: Dp = 12.dp, + showLoading: Boolean = false, isOnClickEnabled: Boolean = true, onClick: () -> Unit = {} ) { - Column( + Row( modifier .fillMaxWidth() .clickable(enabled = isOnClickEnabled) { onClick() } .padding(horizontal = 18.dp, vertical = verticalPadding), - verticalArrangement = Arrangement.Center + verticalAlignment = Alignment.CenterVertically ) { - Text( - title, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface, - fontSize = 18.sp - ) - description?.let { + Column(Modifier.weight(1f)) { Text( - it, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, + title, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface, + fontSize = 18.sp ) + description?.let { + Text( + it, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + + FadeTransitionPreservingSpace(visible = showLoading) { + Row { + Spacer(modifier = Modifier.width(18.dp)) + CircularProgressIndicator( + color = MaterialTheme.colorScheme.onSurface, + strokeCap = StrokeCap.Round, + modifier = Modifier.size(28.dp) + ) + } } } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutState.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutState.kt index 3c32b2e4..4e52a750 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutState.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutState.kt @@ -6,5 +6,7 @@ import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo @Immutable data class AboutState( val showUpdateDialog: Boolean = false, + val alreadyCheckedForUpdates: Boolean = false, + val updateLoading: Boolean = false, val updateInfo: LatestReleaseInfo? = null ) diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutViewModel.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutViewModel.kt index f8aa69d2..ce37344d 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutViewModel.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/data/AboutViewModel.kt @@ -40,9 +40,19 @@ class AboutViewModel @Inject constructor( is AboutEvent.OnCheckForUpdates -> { viewModelScope.launch { + _state.update { + it.copy( + updateLoading = true + ) + } val result = checkForUpdates.execute(false) if (result == null) { + _state.update { + it.copy( + updateLoading = false + ) + } event.error() return@launch } @@ -52,12 +62,19 @@ class AboutViewModel @Inject constructor( if (version == currentVersion) { event.noUpdatesFound() + _state.update { + it.copy( + updateLoading = false, + alreadyCheckedForUpdates = true + ) + } return@launch } _state.update { it.copy( showUpdateDialog = true, + updateLoading = false, updateInfo = result ) } diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt b/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt index 2f532141..17069035 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt @@ -1,5 +1,8 @@ package ua.acclorite.book_story.presentation.ui +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.EaseInOut +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -8,8 +11,11 @@ import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility @@ -86,4 +92,22 @@ fun SlidingTransition( } + fadeOut(tween(100)), content = content ) +} + +@Composable +fun FadeTransitionPreservingSpace( + visible: Boolean, + modifier: Modifier = Modifier, + animationSpec: AnimationSpec = tween(durationMillis = 300, easing = EaseInOut), + content: @Composable () -> Unit +) { + val alpha by animateFloatAsState( + if (visible) 1f else 0f, + label = "", + animationSpec = animationSpec + ) + + Box(modifier = modifier.alpha(alpha)) { + content.invoke() + } } \ No newline at end of file