diff --git a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchApplicationCachesService.kt b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchApplicationCachesService.kt index 5348877b..c211c253 100644 --- a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchApplicationCachesService.kt +++ b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchApplicationCachesService.kt @@ -33,6 +33,8 @@ import kotlin.io.path.div import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.future.future import kotlinx.coroutines.launch @@ -91,11 +93,16 @@ class PackageSearchApplicationCachesService(private val coroutineScope: Coroutin val isOnlineFlow = apiClient.isOnlineFlow() .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), true) - val apiPackageCache = PackageSearchApiPackageCache( - apiPackageCache = packagesRepository, - searchCache = searchesRepository, - apiClient = apiClient - ) + val apiPackageCache = isOnlineFlow + .map { + PackageSearchApiPackageCache( + apiPackageCache = packagesRepository, + searchCache = searchesRepository, + apiClient = apiClient, + isOnline = it + ) + } + .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1) private suspend fun createIndexes() { searchesRepository.createIndex( diff --git a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchProjectService.kt b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchProjectService.kt index 1b0112d5..b53cbcf7 100644 --- a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchProjectService.kt +++ b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/services/PackageSearchProjectService.kt @@ -65,17 +65,20 @@ class PackageSearchProjectService( // Todo SAVE internal val stableOnlyStateFlow = MutableStateFlow(true) - val knownRepositoriesStateFlow = timer(12.hours) { + val knownRepositoriesStateFlow = IntelliJApplication.PackageSearchApplicationCachesService .apiPackageCache - .getKnownRepositories() - .associateBy { it.id } - } - .retry { - logWarn("${this::class.simpleName}#knownRepositoriesStateFlow", throwable = it) - true - } - .stateIn(coroutineScope, SharingStarted.Eagerly, emptyMap()) + .flatMapLatest { caches -> + timer(12.hours) { + caches.getKnownRepositories() + .associateBy { it.id } + } + } + .retry { + logWarn("${this::class.simpleName}#knownRepositoriesStateFlow", throwable = it) + true + } + .stateIn(coroutineScope, SharingStarted.Eagerly, emptyMap()) override val knownRepositories: Map get() = knownRepositoriesStateFlow.value @@ -85,11 +88,14 @@ class PackageSearchProjectService( .stateIn(coroutineScope, SharingStarted.Eagerly, false) private val contextFlow - get() = knownRepositoriesStateFlow.map { repositories -> + get() = combine( + knownRepositoriesStateFlow, + IntelliJApplication.PackageSearchApplicationCachesService.apiPackageCache + ) { repositories, cache -> WindowedModuleBuilderContext( project = project, knownRepositories = repositories, - packagesCache = IntelliJApplication.PackageSearchApplicationCachesService.apiPackageCache, + packagesCache = cache, coroutineScope = coroutineScope, projectCaches = project.PackageSearchProjectCachesService.cache, applicationCaches = IntelliJApplication.PackageSearchApplicationCachesService.cache, diff --git a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/ui/model/packageslist/PackageListViewModel.kt b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/ui/model/packageslist/PackageListViewModel.kt index 6ec2dbe5..6338f2ce 100644 --- a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/ui/model/packageslist/PackageListViewModel.kt +++ b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/ui/model/packageslist/PackageListViewModel.kt @@ -32,6 +32,7 @@ import com.jetbrains.packagesearch.plugin.ui.model.hasUpdates import com.jetbrains.packagesearch.plugin.ui.model.infopanel.InfoPanelViewModel import com.jetbrains.packagesearch.plugin.ui.model.packageslist.PackageListItemEvent.SetHeaderState.TargetState import com.jetbrains.packagesearch.plugin.ui.model.packageslist.PackageListItemEvent.SetHeaderState.TargetState.OPEN +import com.jetbrains.packagesearch.plugin.utils.PackageSearchApiPackageCache import com.jetbrains.packagesearch.plugin.utils.PackageSearchApplicationCachesService import com.jetbrains.packagesearch.plugin.utils.PackageSearchProjectService import com.jetbrains.packagesearch.plugin.utils.logTODO @@ -58,6 +59,7 @@ import kotlinx.coroutines.flow.retry import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.zip import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString @@ -145,15 +147,19 @@ class PackageListViewModel( else -> null } } - .mapLatest { data -> + .zip(IntelliJApplication.PackageSearchApplicationCachesService.apiPackageCache) { a, b -> a to b } + .mapLatest { (data, apis) -> when (data) { null -> emptyMap() else -> { isLoadingChannel.send(true) delay(250.milliseconds) // debounce for mapLatest! when (data.selectedModule) { - is PackageSearchModule.Base -> data.selectedModule.getSearchQuery(data.searchQuery) - is PackageSearchModule.WithVariants -> data.selectedModule.getSearchQueries(data.searchQuery) + is PackageSearchModule.Base -> data.selectedModule.getSearchQuery(data.searchQuery, apis) + is PackageSearchModule.WithVariants -> data.selectedModule.getSearchQueries( + data.searchQuery, + apis + ) } } } @@ -212,11 +218,11 @@ class PackageListViewModel( private suspend fun PackageSearchModule.Base.getSearchQuery( searchQuery: String, + apis: PackageSearchApiPackageCache, ): Map { val headerId = PackageListItem.Header.Id.Remote.Base(identity) val results = Search.Results.Base( - packages = IntelliJApplication.PackageSearchApplicationCachesService.apiPackageCache - .searchPackages(buildSearchParameters { + packages = apis.searchPackages(buildSearchParameters { this.searchQuery = searchQuery packagesType = compatiblePackageTypes }), @@ -232,6 +238,7 @@ class PackageListViewModel( private suspend fun PackageSearchModule.WithVariants.getSearchQueries( searchQuery: String, + apis: PackageSearchApiPackageCache, ): Map = variants.groupByCompatiblePackageTypes() .entries @@ -241,7 +248,8 @@ class PackageListViewModel( in variants.map { it.name } -> 0 else -> 1 } - }.associate { (packagesType, variants) -> + } + .associate { (packagesType, variants) -> val headerId = PackageListItem.Header.Id.Remote.WithVariant(identity, variants.map { it.name }) val primaryVariantName = variants.first { it.isPrimary }.name val attributes = variants.first().attributes.map { it.value } @@ -249,9 +257,7 @@ class PackageListViewModel( val search: Search = when (mainVariantName) { in variants.map { it.name } -> { val results = Search.Results.WithVariants( - packages = IntelliJApplication.PackageSearchApplicationCachesService - .apiPackageCache - .searchPackages { + packages = apis.searchPackages { this.searchQuery = searchQuery this.packagesType = packagesType }, @@ -274,7 +280,7 @@ class PackageListViewModel( this.searchQuery = searchQuery this.packagesType = packagesType }, - apis = IntelliJApplication.PackageSearchApplicationCachesService.apiPackageCache, + apis = apis, attributes = attributes, primaryVariantName = primaryVariantName, additionalVariants = additionalVariants, diff --git a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/utils/PackageSearchApiPackageCache.kt b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/utils/PackageSearchApiPackageCache.kt index c4758283..1a4dee82 100644 --- a/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/utils/PackageSearchApiPackageCache.kt +++ b/plugin/src/main/kotlin/com/jetbrains/packagesearch/plugin/utils/PackageSearchApiPackageCache.kt @@ -28,6 +28,7 @@ class PackageSearchApiPackageCache( private val searchCache: CoroutineObjectRepository, private val apiClient: PackageSearchApi, private val maxAge: Duration = Random.nextDouble(0.5, 1.0).days, + private val isOnline: Boolean, ) : PackageSearchApi by apiClient { private val cachesMutex = Mutex() @@ -74,7 +75,7 @@ class PackageSearchApiPackageCache( .toList() .associateBy { it.id } val missingIds = ids - localDatabaseResults.keys - if (missingIds.isNotEmpty()) { + if (missingIds.isNotEmpty() && isOnline) { val networkResults = apiCall(missingIds) // TODO cache also miss in network to avoid pointless empty query if (networkResults.isNotEmpty()) {