AboutScreen: Added loading indicator to "Check For Updates".

This commit is contained in:
acclorite 2024-08-08 14:43:38 +03:00
parent 69a72c59fc
commit a86163360d
5 changed files with 116 additions and 34 deletions

View file

@ -144,27 +144,36 @@ private fun AboutScreen(
) )
append("\n") append("\n")
append(stringResource(id = R.string.app_version_option_desc_2)) append(stringResource(id = R.string.app_version_option_desc_2))
} },
showLoading = state.value.updateLoading
) { ) {
onEvent( if (!state.value.alreadyCheckedForUpdates) {
AboutEvent.OnCheckForUpdates( onEvent(
context = context, AboutEvent.OnCheckForUpdates(
noUpdatesFound = { context = context,
Toast.makeText( noUpdatesFound = {
context, Toast.makeText(
context.getString(R.string.no_updates), context,
Toast.LENGTH_LONG context.getString(R.string.no_updates),
).show() Toast.LENGTH_LONG
}, ).show()
error = { },
Toast.makeText( error = {
context, Toast.makeText(
context.getString(R.string.error_check_internet), context,
Toast.LENGTH_LONG context.getString(R.string.error_check_internet),
).show() Toast.LENGTH_LONG
} ).show()
}
)
) )
) } else {
Toast.makeText(
context,
context.getString(R.string.no_updates),
Toast.LENGTH_LONG
).show()
}
} }
} }

View file

@ -1,22 +1,38 @@
package ua.acclorite.book_story.presentation.screens.about.components package ua.acclorite.book_story.presentation.screens.about.components
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column 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.fillMaxWidth
import androidx.compose.foundation.layout.padding 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.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp 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 @Composable
fun AboutItem( fun AboutItem(
@ -24,31 +40,45 @@ fun AboutItem(
title: String, title: String,
description: AnnotatedString?, description: AnnotatedString?,
verticalPadding: Dp = 12.dp, verticalPadding: Dp = 12.dp,
showLoading: Boolean = false,
isOnClickEnabled: Boolean = true, isOnClickEnabled: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
Column( Row(
modifier modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(enabled = isOnClickEnabled) { .clickable(enabled = isOnClickEnabled) {
onClick() onClick()
} }
.padding(horizontal = 18.dp, vertical = verticalPadding), .padding(horizontal = 18.dp, vertical = verticalPadding),
verticalArrangement = Arrangement.Center verticalAlignment = Alignment.CenterVertically
) { ) {
Text( Column(Modifier.weight(1f)) {
title,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface,
fontSize = 18.sp
)
description?.let {
Text( Text(
it, title,
overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.bodyLarge,
style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurface,
color = MaterialTheme.colorScheme.onSurfaceVariant, 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)
)
}
} }
} }
} }

View file

@ -6,5 +6,7 @@ import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
@Immutable @Immutable
data class AboutState( data class AboutState(
val showUpdateDialog: Boolean = false, val showUpdateDialog: Boolean = false,
val alreadyCheckedForUpdates: Boolean = false,
val updateLoading: Boolean = false,
val updateInfo: LatestReleaseInfo? = null val updateInfo: LatestReleaseInfo? = null
) )

View file

@ -40,9 +40,19 @@ class AboutViewModel @Inject constructor(
is AboutEvent.OnCheckForUpdates -> { is AboutEvent.OnCheckForUpdates -> {
viewModelScope.launch { viewModelScope.launch {
_state.update {
it.copy(
updateLoading = true
)
}
val result = checkForUpdates.execute(false) val result = checkForUpdates.execute(false)
if (result == null) { if (result == null) {
_state.update {
it.copy(
updateLoading = false
)
}
event.error() event.error()
return@launch return@launch
} }
@ -52,12 +62,19 @@ class AboutViewModel @Inject constructor(
if (version == currentVersion) { if (version == currentVersion) {
event.noUpdatesFound() event.noUpdatesFound()
_state.update {
it.copy(
updateLoading = false,
alreadyCheckedForUpdates = true
)
}
return@launch return@launch
} }
_state.update { _state.update {
it.copy( it.copy(
showUpdateDialog = true, showUpdateDialog = true,
updateLoading = false,
updateInfo = result updateInfo = result
) )
} }

View file

@ -1,5 +1,8 @@
package ua.acclorite.book_story.presentation.ui 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.core.tween
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
@ -8,8 +11,11 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
@ -87,3 +93,21 @@ fun SlidingTransition(
content = content content = content
) )
} }
@Composable
fun FadeTransitionPreservingSpace(
visible: Boolean,
modifier: Modifier = Modifier,
animationSpec: AnimationSpec<Float> = 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()
}
}