diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt index b6988f1a1..4fe24d2d2 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt @@ -309,9 +309,8 @@ private fun ScreenContentApp( index = screen.index, onNavigateUp = ::showMainPane ) - // TODO: Pass the real developerId is Screen.DevProfile -> DevProfileScreen( - developerId = app.developerName, + publisherId = app.developerName, onNavigateUp = ::showMainPane, onNavigateToAppDetails = { onNavigateToAppDetails(it) } ) diff --git a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt index c54be52d7..3e9afb186 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt @@ -5,35 +5,71 @@ 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.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Scaffold import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter 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.gplayapi.data.models.App import com.aurora.store.R 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.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 fun DevProfileScreen( developerId: String, onNavigateUp: () -> 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( - topAppBarTitle = "", + apps = apps, + topAppBarTitle = publisherId, onNavigateUp = onNavigateUp, onNavigateToAppDetails = onNavigateToAppDetails ) @@ -41,7 +77,8 @@ fun DevProfileScreen( @Composable private fun ScreenContent( - topAppBarTitle: String? = null, + apps: LazyPagingItems, + topAppBarTitle: String, onNavigateUp: () -> Unit = {}, onNavigateToAppDetails: (packageName: String) -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() @@ -55,21 +92,43 @@ private fun ScreenContent( ) } ) { paddingValues -> - Column( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(dimensionResource(R.dimen.padding_medium)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) - ) { + when (apps.loadState.refresh) { + is LoadState.Loading -> {} + 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 @Composable -private fun DevProfileScreenPreview() { - ScreenContent() +@OptIn(ExperimentalCoilApi::class) +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 + ) + } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt index fdc1d3a49..b17e47b97 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/search/SearchResultViewModel.kt @@ -23,6 +23,8 @@ import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel 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.StreamCluster 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 dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -54,6 +58,9 @@ class SearchResultViewModel @Inject constructor( private val contract: SearchContract get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper + private val _apps = MutableStateFlow>(PagingData.empty()) + val apps = _apps.asStateFlow() + private val stashMutex = Mutex() @Synchronized