Implement Compose dev profile screen

The DevProfileScreen(developerId) overload was an empty stub, so
play.google.com/store/apps/dev?id= deep links rendered a black screen.
Render the developer's stream bundle via the existing StreamCarousel,
observing DevProfileViewModel for loading, error/retry and per-cluster
pagination.
This commit is contained in:
Rahul Patel
2026-05-31 01:47:24 +05:30
parent f1064ceab9
commit e1b8b5ae95

View File

@@ -15,6 +15,8 @@ import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfoV2 import androidx.compose.material3.adaptive.currentWindowAdaptiveInfoV2
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -30,14 +32,19 @@ import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey import androidx.paging.compose.itemKey
import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.adaptiveNavigationIcon
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.data.models.details.DevStream
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.Placeholder
import com.aurora.store.compose.composable.StreamCarousel
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.data.model.ViewState
import com.aurora.store.viewmodel.details.DevProfileViewModel import com.aurora.store.viewmodel.details.DevProfileViewModel
import com.aurora.store.viewmodel.search.SearchViewModel import com.aurora.store.viewmodel.search.SearchViewModel
import kotlin.random.Random import kotlin.random.Random
@@ -53,7 +60,61 @@ fun DevProfileScreen(
onNavigateTo: (Destination) -> Unit, onNavigateTo: (Destination) -> Unit,
viewModel: DevProfileViewModel = hiltViewModel() viewModel: DevProfileViewModel = hiltViewModel()
) { ) {
// TODO: Implement when migrating logic for current DevProfileFragment val state by viewModel.liveData.observeAsState()
LaunchedEffect(developerId) { viewModel.getStreamBundle(developerId) }
val devStream = (state as? ViewState.Success<*>)?.data as? DevStream
DevStreamContent(
title = devStream?.title?.takeIf { it.isNotBlank() } ?: developerId,
streamBundle = devStream?.streamBundle,
isError = state is ViewState.Error,
onRetry = { viewModel.getStreamBundle(developerId) },
onClusterScrolled = viewModel::observeCluster,
onNavigateTo = onNavigateTo
)
}
@Composable
private fun DevStreamContent(
title: String,
streamBundle: StreamBundle?,
isError: Boolean,
onRetry: () -> Unit = {},
onClusterScrolled: (StreamCluster) -> Unit = {},
onNavigateTo: (Destination) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) {
Scaffold(
topBar = {
TopAppBar(
title = title,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
)
}
) { paddingValues ->
if (isError) {
Placeholder(
modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_refresh),
message = stringResource(R.string.error),
actionLabel = stringResource(R.string.action_retry),
onAction = onRetry
)
} else {
// A null bundle renders the loading shimmer until the stream resolves
StreamCarousel(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize(),
streamBundle = streamBundle,
onHeaderClick = { onNavigateTo(Destination.StreamBrowse(it)) },
onAppClick = { onNavigateTo(Destination.AppDetails(it.packageName)) },
onClusterScrolled = onClusterScrolled
)
}
}
} }
/** /**