Skip to content

Commit

Permalink
Add 'No Packages Found' view to Package List UI (#159) (#163)
Browse files Browse the repository at this point in the history
* Add 'No Packages Found' view to Package List UI (#159)

This commit introduces a new view in the package search results for the case when no packages are found. The update includes a 'NoPackagesFoundItem' Composable function, and respective user interface changes. The 'NoPackagesFound' class added to the 'PackageListItem' differentiates between errors and no results in package searches.

(cherry picked from commit f8e8135)

* Remove unused imports in ProjectServiceTest

Cleaned up PackageSearchProjectServiceTest file by removing unnecessary imports. This helps enhance code readability and maintainability by removing clutter from the source file.

---------

Co-authored-by: Lamberto Basti <[email protected]>
  • Loading branch information
fscarponi and lamba92 authored May 10, 2024
1 parent b8cbc18 commit ac223d3
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,19 @@ class PackageListBuilder(
dependenciesToShow
.filter { it.matchesSearchQuery() }
.forEach { dependency ->
addDeclaredPackage(
title = dependency.displayName,
subtitle = dependency.coordinates,
id = PackageListItem.Package.Declared.Id.Base(base.identity, dependency.id),
icon = dependency.icon,
latestVersion = dependency.getLatestVersion(onlyStable)?.versionName,
selectedScope = dependency.declaredScope,
availableScopes = base.availableScopes,
declaredVersion = dependency.declaredVersion?.versionName,
availableVersions = dependency.getAvailableVersionStrings(),
allowMissingScope = !base.dependencyMustHaveAScope,
)
}
addDeclaredPackage(
title = dependency.displayName,
subtitle = dependency.coordinates,
id = PackageListItem.Package.Declared.Id.Base(base.identity, dependency.id),
icon = dependency.icon,
latestVersion = dependency.getLatestVersion(onlyStable)?.versionName,
selectedScope = dependency.declaredScope,
availableScopes = base.availableScopes,
declaredVersion = dependency.declaredVersion?.versionName,
availableVersions = dependency.getAvailableVersionStrings(),
allowMissingScope = !base.dependencyMustHaveAScope,
)
}
}
}

Expand Down Expand Up @@ -193,23 +193,23 @@ class PackageListBuilder(
variant.declaredDependencies
.filter { it.matchesSearchQuery() }
.forEach { dependency ->
addDeclaredPackage(
title = dependency.displayName,
subtitle = dependency.coordinates,
id = PackageListItem.Package.Declared.Id.WithVariant(
moduleIdentity = module.identity,
packageId = dependency.id,
variantName = variant.name,
),
icon = dependency.icon,
latestVersion = dependency.getLatestVersion(onlyStable)?.versionName,
selectedScope = dependency.declaredScope,
availableScopes = variant.availableScopes,
declaredVersion = dependency.declaredVersion?.versionName,
availableVersions = dependency.getAvailableVersionStrings(),
allowMissingScope = !module.dependencyMustHaveAScope,
)
}
addDeclaredPackage(
title = dependency.displayName,
subtitle = dependency.coordinates,
id = PackageListItem.Package.Declared.Id.WithVariant(
moduleIdentity = module.identity,
packageId = dependency.id,
variantName = variant.name,
),
icon = dependency.icon,
latestVersion = dependency.getLatestVersion(onlyStable)?.versionName,
selectedScope = dependency.declaredScope,
availableScopes = variant.availableScopes,
declaredVersion = dependency.declaredVersion?.versionName,
availableVersions = dependency.getAvailableVersionStrings(),
allowMissingScope = !module.dependencyMustHaveAScope,
)
}
}
}
}
Expand All @@ -235,23 +235,23 @@ class PackageListBuilder(
dependenciesToShow
.filter { it.second.matchesSearchQuery() }
.forEach { (variant, dependency) ->
addDeclaredPackage(
title = dependency.displayName,
subtitle = variant.name,
id = PackageListItem.Package.Declared.Id.WithVariant(
module.identity,
dependency.id,
variant.name,
),
icon = dependency.icon,
latestVersion = dependency.getLatestVersion(onlyStable)?.versionName,
selectedScope = dependency.declaredScope,
availableScopes = variant.availableScopes,
declaredVersion = dependency.declaredVersion?.versionName,
availableVersions = dependency.getAvailableVersionStrings(),
allowMissingScope = !module.dependencyMustHaveAScope,
)
}
addDeclaredPackage(
title = dependency.displayName,
subtitle = variant.name,
id = PackageListItem.Package.Declared.Id.WithVariant(
module.identity,
dependency.id,
variant.name,
),
icon = dependency.icon,
latestVersion = dependency.getLatestVersion(onlyStable)?.versionName,
selectedScope = dependency.declaredScope,
availableScopes = variant.availableScopes,
declaredVersion = dependency.declaredVersion?.versionName,
availableVersions = dependency.getAvailableVersionStrings(),
allowMissingScope = !module.dependencyMustHaveAScope,
)
}
}
}

Expand All @@ -278,17 +278,31 @@ class PackageListBuilder(
additionalContent = search.buildVariantsText()
)

is Search.Response.Base.Success -> addFromSearchQueryBase(
headerId = headerId as PackageListItem.Header.Id.Remote.Base,
search = search,
module = modulesMap[headerId.moduleIdentity] as? PackageSearchModule.Base ?: return@forEach
)
is Search.Response.Base.Success -> when {
search.packages.isNotEmpty() -> addFromSearchQueryBase(
headerId = headerId as PackageListItem.Header.Id.Remote.Base,
search = search,
module = modulesMap[headerId.moduleIdentity] as? PackageSearchModule.Base ?: return@forEach
)

else -> addSearchResultNoPackages(headerId = headerId)
}

is Search.Response.WithVariants.Success -> when {
search.packages.isNotEmpty() -> addFromSearchQueryWithVariants(
headerId = headerId as PackageListItem.Header.Id.Remote.WithVariant,
search = search,
module = modulesMap[headerId.moduleIdentity] as? PackageSearchModule.WithVariants
?: return@forEach
)

else -> addSearchResultNoPackages(
headerId = headerId,
additionalContent = search.buildVariantsText(),
attributes = search.attributes
)
}

is Search.Response.WithVariants.Success -> addFromSearchQueryWithVariants(
headerId = headerId as PackageListItem.Header.Id.Remote.WithVariant,
search = search,
module = modulesMap[headerId.moduleIdentity] as? PackageSearchModule.WithVariants ?: return@forEach
)

is Search.Response.Base.Error -> addSearchResultError(headerId = headerId)

Expand All @@ -301,6 +315,32 @@ class PackageListBuilder(
}
}

private fun addSearchResultNoPackages(
headerId: PackageListItem.Header.Id.Remote,
attributes: List<String> = emptyList(),
additionalContent: PackageListItem.Header.AdditionalContent.VariantsText? = null,
) {
val headerState = when (headerCollapsedStates[headerId]) {
TargetState.OPEN -> PackageListItem.Header.State.OPEN
else -> PackageListItem.Header.State.CLOSED
}
addHeader(
title = PackageSearchBundle.message("packagesearch.ui.toolwindow.tab.packages.searchResults"),
id = headerId,
state = headerState,
attributes = attributes,
additionalContent = additionalContent,
)
items.add(
PackageListItem.NoPackagesFound(
id = PackageListItem.NoPackagesFound.Id(
moduleIdentity = headerId.moduleIdentity,
parentHeaderId = headerId
)
)
)
}

private fun addSearchResultError(
headerId: PackageListItem.Header.Id.Remote,
attributes: List<String> = emptyList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,13 @@ sealed interface PackageListItem {
) : PackageListItem.Id
}

data class NoPackagesFound(override val id: Id) : PackageListItem {

@Serializable
data class Id(
override val moduleIdentity: PackageSearchModule.Identity,
val parentHeaderId: Header.Id,
) : PackageListItem.Id
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ import com.jetbrains.packagesearch.plugin.ui.model.packageslist.PackageListItemE
import com.jetbrains.packagesearch.plugin.ui.model.packageslist.PackageListItemEvent.OnPackageAction.Install
import com.jetbrains.packagesearch.plugin.ui.model.packageslist.PackageListItemEvent.OnPackageAction.Remove
import com.jetbrains.packagesearch.plugin.ui.panels.packages.items.PackageListHeader
import java.net.Socket
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import org.jetbrains.jewel.foundation.lazy.SelectableLazyColumn
import org.jetbrains.jewel.foundation.lazy.SelectableLazyItemScope
import org.jetbrains.jewel.foundation.lazy.SelectableLazyListState
Expand Down Expand Up @@ -110,6 +125,10 @@ fun PackageSearchPackageList(
onLinkClick = { onPackageEvent(PackageListItemEvent.OnRetryPackageSearch(item.id)) }
)
}

is PackageListItem.NoPackagesFound -> item(key = item.id, contentType = item.contentType()) {
NoPackagesFoundItem()
}
}
}
}
Expand All @@ -127,6 +146,17 @@ fun SearchErrorItem(onLinkClick: () -> Unit) {
}
}

@Composable
fun NoPackagesFoundItem() {
Column(
Modifier.fillMaxSize().padding(20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(message("packagesearch.ui.toolwindow.tab.packages.searchResults.noPackages"))
}
}

@Composable
internal fun SelectableLazyItemScope.PackageListItem(
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -158,7 +188,7 @@ internal fun SelectableLazyItemScope.PackageListItem(
onDoubleClick = { onPackageListItemEvent(OnPackageDoubleClick(content.id)) },
onClick = {
//this event should be handled when you click on a selected package to refresh side panel content
if (isSelected ) onPackageListItemEvent(
if (isSelected) onPackageListItemEvent(
PackageListItemEvent.InfoPanelEvent.OnSelectedPackageClick(content.id)
)
}
Expand Down Expand Up @@ -344,7 +374,7 @@ internal fun RemotePackageWithVariantsActionPopup(
}
if (!isInstalledInPrimaryVariant) {
passiveItem {
Divider(Orientation.Horizontal,modifier = Modifier.padding(vertical = 4.dp))
Divider(Orientation.Horizontal, modifier = Modifier.padding(vertical = 4.dp))
}
selectableItem(
selected = false,
Expand All @@ -356,7 +386,7 @@ internal fun RemotePackageWithVariantsActionPopup(

if (additionalVariants.isNotEmpty()) {
passiveItem {
Divider(Orientation.Horizontal,modifier = Modifier.padding(vertical = 4.dp))
Divider(Orientation.Horizontal, modifier = Modifier.padding(vertical = 4.dp))
}
additionalVariants.forEach {
selectableItem(
Expand Down Expand Up @@ -400,7 +430,7 @@ internal fun DeclaredPackageActionPopup(
}
}
passiveItem {
Divider(Orientation.Horizontal,modifier = Modifier.padding(vertical = 4.dp))
Divider(Orientation.Horizontal, modifier = Modifier.padding(vertical = 4.dp))
}
selectableItem(
selected = false,
Expand All @@ -424,6 +454,7 @@ private fun PackageListItem.contentType() = when (this) {
is PackageListItem.Package.Declared -> "declared.package"
is PackageListItem.Package.Remote -> "remote.package"
is PackageListItem.SearchError -> "search.error"
is PackageListItem.NoPackagesFound -> "no.packages.found"
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ packagesearch.ui.toolwindow.packages.sort.by=Sort by:
packagesearch.ui.toolwindow.tab.packages.installedPackages.addedIn=Added in {0}
packagesearch.ui.toolwindow.tab.packages.searchResults=Search Results
packagesearch.ui.toolwindow.tab.packages.searchResults.error=An error occurred while searching for dependencies.
packagesearch.ui.toolwindow.tab.packages.searchResults.noPackages=No packages found.
packagesearch.ui.toolwindow.tab.packages.searchResults.error.retry=Try again
packagesearch.ui.toolwindow.tab.packages.selectedPackage=Selected dependency
packagesearch.ui.toolwindow.tab.packages.title=Manage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.jetbrains.packagesearch.plugin.tests.projectservice

import com.intellij.ide.starter.junit5.JUnit5StarterAssistant
import com.intellij.ide.starter.runner.TestContainerImpl
import com.intellij.testIntegration.TestFailedLineManager
import com.intellij.tools.ide.performanceTesting.commands.CommandChain
import com.intellij.tools.ide.performanceTesting.commands.exitApp
import com.intellij.tools.ide.performanceTesting.commands.waitForSmartMode
Expand Down Expand Up @@ -31,8 +30,6 @@ import org.junit.jupiter.params.provider.MethodSource
@ExtendWith(JUnit5StarterAssistant::class)
abstract class PackageSearchProjectServiceTest {



abstract fun getContext(): TestContainerImpl

abstract val resourcePath: String
Expand Down

0 comments on commit ac223d3

Please sign in to comment.