compose: details: Initial DevProfileScreen implementation

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2025-05-16 21:07:09 +08:00
parent 3a23088525
commit f868a0f505
3 changed files with 84 additions and 19 deletions

View File

@@ -309,9 +309,8 @@ private fun ScreenContentApp(
index = screen.index, index = screen.index,
onNavigateUp = ::showMainPane onNavigateUp = ::showMainPane
) )
// TODO: Pass the real developerId
is Screen.DevProfile -> DevProfileScreen( is Screen.DevProfile -> DevProfileScreen(
developerId = app.developerName, publisherId = app.developerName,
onNavigateUp = ::showMainPane, onNavigateUp = ::showMainPane,
onNavigateToAppDetails = { onNavigateToAppDetails(it) } onNavigateToAppDetails = { onNavigateToAppDetails(it) }
) )

View File

@@ -5,35 +5,71 @@
package com.aurora.store.compose.ui.dev package com.aurora.store.compose.ui.dev
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
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.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.LoadState
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import coil3.annotation.ExperimentalCoilApi
import coil3.compose.LocalAsyncImagePreviewHandler
import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.adaptiveNavigationIcon
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composables.TopAppBarComposable import com.aurora.store.compose.composables.TopAppBarComposable
import com.aurora.store.compose.composables.app.AppListComposable
import com.aurora.store.compose.composables.preview.AppPreviewProvider
import com.aurora.store.compose.composables.preview.coilPreviewProvider
import com.aurora.store.viewmodel.details.DevProfileViewModel import com.aurora.store.viewmodel.details.DevProfileViewModel
import com.aurora.store.viewmodel.search.SearchResultViewModel
import kotlinx.coroutines.flow.flowOf
import kotlin.random.Random
/**
* Screen to display apps from a developer using the developerId
* for e.g. https://play.google.com/store/apps/dev?id=5700313618786177705
*/
@Composable @Composable
fun DevProfileScreen( fun DevProfileScreen(
developerId: String, developerId: String,
onNavigateUp: () -> Unit, onNavigateUp: () -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit, onNavigateToAppDetails: (packageName: String) -> Unit,
devProfileViewModel: DevProfileViewModel = hiltViewModel() viewModel: DevProfileViewModel = hiltViewModel()
) { ) {
// TODO: Implement when migrating logic for current DevProfileFragment
}
/**
* Screen to display apps from a developer by searching using the publisherId
* for e.g. https://play.google.com/store/apps/developer?id=The+Tor+Project
*/
@Composable
fun DevProfileScreen(
publisherId: String,
onNavigateUp: () -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
viewModel: SearchResultViewModel = hiltViewModel()
) {
val apps = viewModel.apps.collectAsLazyPagingItems()
LaunchedEffect(key1 = Unit) { viewModel.search("pub:$publisherId") }
ScreenContent( ScreenContent(
topAppBarTitle = "", apps = apps,
topAppBarTitle = publisherId,
onNavigateUp = onNavigateUp, onNavigateUp = onNavigateUp,
onNavigateToAppDetails = onNavigateToAppDetails onNavigateToAppDetails = onNavigateToAppDetails
) )
@@ -41,7 +77,8 @@ fun DevProfileScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
topAppBarTitle: String? = null, apps: LazyPagingItems<App>,
topAppBarTitle: String,
onNavigateUp: () -> Unit = {}, onNavigateUp: () -> Unit = {},
onNavigateToAppDetails: (packageName: String) -> Unit = {}, onNavigateToAppDetails: (packageName: String) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
@@ -55,21 +92,43 @@ private fun ScreenContent(
) )
} }
) { paddingValues -> ) { paddingValues ->
Column( when (apps.loadState.refresh) {
modifier = Modifier is LoadState.Loading -> {}
.padding(paddingValues)
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(dimensionResource(R.dimen.padding_medium)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
) {
is LoadState.Error -> {}
else -> {
LazyColumn(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium))
) {
items(count = apps.itemCount, key = apps.itemKey { it.id }) { index ->
apps[index]?.let { app ->
AppListComposable(
app = app,
onClick = { onNavigateToAppDetails(app.packageName) }
)
}
}
}
}
} }
} }
} }
@Preview @Preview
@Composable @Composable
private fun DevProfileScreenPreview() { @OptIn(ExperimentalCoilApi::class)
ScreenContent() private fun DevProfileScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
CompositionLocalProvider(LocalAsyncImagePreviewHandler provides coilPreviewProvider) {
val apps = List(10) { app.copy(id = Random.nextInt()) }
val pagedApps = flowOf(PagingData.from(apps)).collectAsLazyPagingItems()
ScreenContent(
topAppBarTitle = app.developerName,
apps = pagedApps
)
}
} }

View File

@@ -23,6 +23,8 @@ import android.util.Log
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.helpers.SearchHelper import com.aurora.gplayapi.helpers.SearchHelper
@@ -33,6 +35,8 @@ import com.aurora.store.data.model.ViewState
import com.aurora.store.data.providers.AuthProvider import com.aurora.store.data.providers.AuthProvider
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
@@ -54,6 +58,9 @@ class SearchResultViewModel @Inject constructor(
private val contract: SearchContract private val contract: SearchContract
get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper
private val _apps = MutableStateFlow<PagingData<App>>(PagingData.empty())
val apps = _apps.asStateFlow()
private val stashMutex = Mutex() private val stashMutex = Mutex()
@Synchronized @Synchronized