compose: details: Show featured reviews again (review summary)
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.composables
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Pager indicator
|
||||
* Adapted from [Pager in Compose](https://developer.android.com/jetpack/compose/layouts/pager#add-page)
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param totalPages Total number of pages
|
||||
* @param currentPage Currently displayed page number
|
||||
*/
|
||||
@Composable
|
||||
fun PageIndicatorComposable(
|
||||
modifier: Modifier = Modifier,
|
||||
totalPages: Int,
|
||||
currentPage: Int = 0,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_xsmall),
|
||||
Alignment.CenterHorizontally
|
||||
)
|
||||
) {
|
||||
repeat(totalPages) { iteration ->
|
||||
val isSelected = currentPage == iteration
|
||||
val color by animateColorAsState(
|
||||
targetValue = if (isSelected) {
|
||||
Color.DarkGray
|
||||
} else {
|
||||
Color.LightGray
|
||||
},
|
||||
animationSpec = tween()
|
||||
)
|
||||
val size by animateDpAsState(
|
||||
targetValue = if (isSelected) {
|
||||
dimensionResource(R.dimen.radius_normal)
|
||||
} else {
|
||||
dimensionResource(R.dimen.radius_small)
|
||||
},
|
||||
animationSpec = tween()
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(size)
|
||||
.clip(CircleShape)
|
||||
.background(color = color)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PageIndicatorComposablePreview() {
|
||||
PageIndicatorComposable(totalPages = 5)
|
||||
}
|
||||
@@ -88,7 +88,8 @@ fun ReviewComposable(modifier: Modifier = Modifier, review: Review) {
|
||||
)
|
||||
Text(
|
||||
text = review.comment,
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.aurora.extensions.browse
|
||||
import com.aurora.extensions.share
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.Review
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composables.HeaderComposable
|
||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
||||
@@ -62,7 +63,7 @@ import com.aurora.store.compose.ui.details.components.AppDataSafety
|
||||
import com.aurora.store.compose.ui.details.components.AppDetails
|
||||
import com.aurora.store.compose.ui.details.components.AppDeveloperDetails
|
||||
import com.aurora.store.compose.ui.details.components.AppPrivacy
|
||||
import com.aurora.store.compose.ui.details.components.AppReviews
|
||||
import com.aurora.store.compose.ui.details.components.AppRatingAndReviews
|
||||
import com.aurora.store.compose.ui.details.components.AppScreenshots
|
||||
import com.aurora.store.compose.ui.details.components.AppTags
|
||||
import com.aurora.store.compose.ui.details.components.AppTesting
|
||||
@@ -89,6 +90,7 @@ fun AppDetailsScreen(
|
||||
val context = LocalContext.current
|
||||
|
||||
val app by viewModel.app.collectAsStateWithLifecycle()
|
||||
val featuredReviews by viewModel.featuredReviews.collectAsStateWithLifecycle()
|
||||
val favorite by viewModel.favourite.collectAsStateWithLifecycle()
|
||||
val exodusReport by viewModel.exodusReport.collectAsStateWithLifecycle()
|
||||
val dataSafetyReport by viewModel.dataSafetyReport.collectAsStateWithLifecycle()
|
||||
@@ -132,6 +134,7 @@ fun AppDetailsScreen(
|
||||
} else {
|
||||
ScreenContentApp(
|
||||
app = this,
|
||||
featuredReviews = featuredReviews,
|
||||
isFavorite = favorite,
|
||||
isAnonymous = viewModel.authProvider.isAnonymous,
|
||||
download = download,
|
||||
@@ -204,6 +207,7 @@ private fun ScreenContentError(onNavigateUp: () -> Unit = {}) {
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
private fun ScreenContentApp(
|
||||
app: App,
|
||||
featuredReviews: List<Review> = emptyList(),
|
||||
isFavorite: Boolean = false,
|
||||
isAnonymous: Boolean = true,
|
||||
download: Download? = null,
|
||||
@@ -262,6 +266,7 @@ private fun ScreenContentApp(
|
||||
AnimatedPane {
|
||||
ScreenContentAppMainPane(
|
||||
app = app,
|
||||
featuredReviews = featuredReviews,
|
||||
download = download,
|
||||
installProgress = installProgress,
|
||||
isAnonymous = isAnonymous,
|
||||
@@ -329,6 +334,7 @@ private fun ScreenContentApp(
|
||||
@Composable
|
||||
private fun ScreenContentAppMainPane(
|
||||
app: App,
|
||||
featuredReviews: List<Review> = emptyList(),
|
||||
download: Download?,
|
||||
installProgress: Float?,
|
||||
isAnonymous: Boolean,
|
||||
@@ -443,7 +449,11 @@ private fun ScreenContentAppMainPane(
|
||||
onNavigateToScreenshot = onNavigateToDetailsScreenshot
|
||||
)
|
||||
|
||||
AppReviews(rating = app.rating, onNavigateToDetailsReview = onNavigateToDetailsReview)
|
||||
AppRatingAndReviews(
|
||||
rating = app.rating,
|
||||
featuredReviews = featuredReviews,
|
||||
onNavigateToDetailsReview = onNavigateToDetailsReview
|
||||
)
|
||||
|
||||
if (!isAnonymous && app.testingProgram?.isAvailable == true) {
|
||||
AppTesting(
|
||||
|
||||
@@ -5,11 +5,21 @@
|
||||
|
||||
package com.aurora.store.compose.ui.details.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
|
||||
@@ -17,18 +27,24 @@ import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.window.core.layout.WindowWidthSizeClass
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.Rating
|
||||
import com.aurora.gplayapi.data.models.Review
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composables.HeaderComposable
|
||||
import com.aurora.store.compose.composables.PageIndicatorComposable
|
||||
import com.aurora.store.compose.composables.details.RatingComposable
|
||||
import com.aurora.store.compose.composables.details.ReviewComposable
|
||||
import com.aurora.store.compose.composables.preview.AppPreviewProvider
|
||||
import java.util.Locale
|
||||
|
||||
@@ -36,12 +52,14 @@ import java.util.Locale
|
||||
* Composable to display reviews of the app, supposed to be used as a part
|
||||
* of the Column with proper vertical arrangement spacing in the AppDetailsScreen.
|
||||
* @param rating Rating of the app
|
||||
* @param featuredReviews Featured app reviews
|
||||
* @param onNavigateToDetailsReview Callback when the user navigates
|
||||
* @param windowAdaptiveInfo Adaptive window information
|
||||
*/
|
||||
@Composable
|
||||
fun AppReviews(
|
||||
fun AppRatingAndReviews(
|
||||
rating: Rating,
|
||||
featuredReviews: List<Review> = emptyList(),
|
||||
onNavigateToDetailsReview: () -> Unit = {},
|
||||
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||
) {
|
||||
@@ -98,12 +116,38 @@ fun AppReviews(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (featuredReviews.isNotEmpty()) {
|
||||
val pagerState = rememberPagerState { featuredReviews.size }
|
||||
HorizontalPager(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||
state = pagerState,
|
||||
pageSpacing = dimensionResource(R.dimen.margin_normal)
|
||||
) { page ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(color = MaterialTheme.colorScheme.secondaryContainer)
|
||||
.requiredHeight(dimensionResource(R.dimen.review_height))
|
||||
) {
|
||||
ReviewComposable(review = featuredReviews[page])
|
||||
}
|
||||
}
|
||||
|
||||
PageIndicatorComposable(
|
||||
totalPages = featuredReviews.size,
|
||||
currentPage = pagerState.currentPage
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun AppReviewsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))) {
|
||||
AppReviews(rating = app.rating)
|
||||
AppRatingAndReviews(rating = app.rating)
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,9 @@ class AppDetailsViewModel @Inject constructor(
|
||||
private val _suggestions = MutableStateFlow<List<App>>(emptyList())
|
||||
val suggestions = _suggestions.asStateFlow()
|
||||
|
||||
private val _featuredReviews = MutableStateFlow<List<Review>>(emptyList())
|
||||
val featuredReviews = _featuredReviews.asStateFlow()
|
||||
|
||||
private val _userReview = MutableStateFlow<Review?>(null)
|
||||
val userReview = _userReview.asStateFlow()
|
||||
|
||||
@@ -157,6 +160,7 @@ class AppDetailsViewModel @Inject constructor(
|
||||
if (throwable != null) return@invokeOnCompletion
|
||||
|
||||
fetchFavourite(packageName)
|
||||
fetchFeaturedReviews(packageName)
|
||||
fetchDataSafetyReport(packageName)
|
||||
fetchSuggestions()
|
||||
fetchExodusPrivacyReport(packageName)
|
||||
@@ -166,6 +170,18 @@ class AppDetailsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchFeaturedReviews(packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
_featuredReviews.value = reviewsHelper.getReviewSummary(packageName)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch featured app reviews", exception)
|
||||
_featuredReviews.value = emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun updateTestingProgramStatus(packageName: String, subscribe: Boolean) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user