diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt index fcef09da8..ab1b77150 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt @@ -27,6 +27,9 @@ abstract class MangaSourcesDao { @Query("SELECT * FROM sources WHERE added_in >= :version") abstract suspend fun findAllFromVersion(version: Int): List + @Query("SELECT * FROM sources ORDER BY used_at DESC LIMIT :limit") + abstract suspend fun findLastUsed(limit: Int): List + @Query("SELECT * FROM sources ORDER BY pinned DESC, sort_key") abstract fun observeAll(): Flow> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/SearchSuggestionType.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/SearchSuggestionType.kt index ab3cb3c49..f25ef6f99 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/SearchSuggestionType.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/SearchSuggestionType.kt @@ -12,5 +12,6 @@ enum class SearchSuggestionType( QUERIES_SUGGEST(R.string.suggested_queries), MANGA(R.string.content_type_manga), SOURCES(R.string.remote_sources), + RECENT_SOURCES(R.string.recent_sources), AUTHORS(R.string.authors), } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt index c3e4b7449..1021e6d70 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt @@ -59,6 +59,11 @@ class MangaSourcesRepository @Inject constructor( } } + suspend fun getTopSources(limit: Int): List { + assimilateNewSources() + return dao.findLastUsed(limit).toSources(settings.isNsfwContentDisabled, null) + } + suspend fun getDisabledSources(): Set { assimilateNewSources() val result = EnumSet.copyOf(remoteSources) @@ -242,7 +247,9 @@ class MangaSourcesRepository @Inject constructor( } suspend fun trackUsage(source: MangaSource) { - dao.setLastUsed(source.name, System.currentTimeMillis()) + if (!settings.isIncognitoModeEnabled && !(settings.isHistoryExcludeNsfw && source.isNsfw())) { + dao.setLastUsed(source.name, System.currentTimeMillis()) + } } private suspend fun setSourcesEnabledImpl(sources: Collection, isEnabled: Boolean) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt index 3adc10d2a..e3df45981 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt @@ -125,6 +125,8 @@ class MangaSearchRepository @Inject constructor( return db.getTagsDao().findRareTags(source.name, limit).toMangaTagsList() } + suspend fun getSourcesSuggestion(limit: Int): List = sourcesRepository.getTopSources(limit) + fun getSourcesSuggestion(query: String, limit: Int): List { if (query.length < 3) { return emptyList() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt index 315cb8ec8..36f952dc1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt @@ -37,6 +37,7 @@ private const val MAX_HINTS_ITEMS = 3 private const val MAX_AUTHORS_ITEMS = 2 private const val MAX_TAGS_ITEMS = 8 private const val MAX_SOURCES_ITEMS = 6 +private const val MAX_SOURCES_TIPS_ITEMS = 2 @HiltViewModel class SearchSuggestionViewModel @Inject constructor( @@ -149,12 +150,18 @@ class SearchSuggestionViewModel @Inject constructor( } else { null } + val sourcesTipsDeferred = if (searchQuery.isEmpty() && SearchSuggestionType.RECENT_SOURCES in types) { + async { repository.getSourcesSuggestion(MAX_SOURCES_TIPS_ITEMS) } + } else { + null + } val tags = tagsDeferred?.await() val mangaList = mangaDeferred?.await() val queries = queriesDeferred?.await() val hints = hintsDeferred?.await() val authors = authorsDeferred?.await() + val sourcesTips = sourcesTipsDeferred?.await() buildList(queries.sizeOrZero() + sources.sizeOrZero() + authors.sizeOrZero() + hints.sizeOrZero() + 2) { if (!tags.isNullOrEmpty()) { @@ -167,6 +174,7 @@ class SearchSuggestionViewModel @Inject constructor( queries?.mapTo(this) { SearchSuggestionItem.RecentQuery(it) } authors?.mapTo(this) { SearchSuggestionItem.Author(it) } hints?.mapTo(this) { SearchSuggestionItem.Hint(it) } + sourcesTips?.mapTo(this) { SearchSuggestionItem.SourceTip(it) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt index 3152927f0..9b923e571 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt @@ -18,6 +18,7 @@ class SearchSuggestionAdapter( delegatesManager .addDelegate(SEARCH_SUGGESTION_ITEM_TYPE_QUERY, searchSuggestionQueryAD(listener)) .addDelegate(searchSuggestionSourceAD(coil, lifecycleOwner, listener)) + .addDelegate(searchSuggestionSourceTipAD(coil, lifecycleOwner, listener)) .addDelegate(searchSuggestionTagsAD(listener)) .addDelegate(searchSuggestionMangaListAD(coil, lifecycleOwner, listener)) .addDelegate(searchSuggestionQueryHintAD(listener)) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceTipAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceTipAD.kt new file mode 100644 index 000000000..55a71a06d --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceTipAD.kt @@ -0,0 +1,43 @@ +package org.koitharu.kotatsu.search.ui.suggestion.adapter + +import androidx.lifecycle.LifecycleOwner +import coil.ImageLoader +import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.getSummary +import org.koitharu.kotatsu.core.model.getTitle +import org.koitharu.kotatsu.core.parser.favicon.faviconUri +import org.koitharu.kotatsu.core.ui.image.FaviconDrawable +import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.newImageRequest +import org.koitharu.kotatsu.core.util.ext.source +import org.koitharu.kotatsu.databinding.ItemSearchSuggestionSourceTipBinding +import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener +import org.koitharu.kotatsu.search.ui.suggestion.model.SearchSuggestionItem + +fun searchSuggestionSourceTipAD( + coil: ImageLoader, + lifecycleOwner: LifecycleOwner, + listener: SearchSuggestionListener, +) = + adapterDelegateViewBinding( + { inflater, parent -> ItemSearchSuggestionSourceTipBinding.inflate(inflater, parent, false) }, + ) { + + binding.root.setOnClickListener { + listener.onSourceClick(item.source) + } + + bind { + binding.textViewTitle.text = item.source.getTitle(context) + binding.textViewSubtitle.text = item.source.getSummary(context) + val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name) + binding.imageViewCover.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run { + fallback(fallbackIcon) + placeholder(fallbackIcon) + error(fallbackIcon) + source(item.source) + enqueueWith(coil) + } + } + } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt index 5493fb920..f1c997956 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.search.ui.suggestion.model +import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.list.ui.ListModelDiffCallback import org.koitharu.kotatsu.list.ui.model.ListModel @@ -69,6 +70,18 @@ sealed interface SearchSuggestionItem : ListModel { } } + data class SourceTip( + val source: MangaSource, + ) : SearchSuggestionItem { + + val isNsfw: Boolean + get() = source.isNsfw() + + override fun areItemsTheSame(other: ListModel): Boolean { + return other is Source && other.source == source + } + } + data class Tags( val tags: List, ) : SearchSuggestionItem { diff --git a/app/src/main/res/layout/item_search_suggestion_source_tip.xml b/app/src/main/res/layout/item_search_suggestion_source_tip.xml new file mode 100644 index 000000000..f3a9da423 --- /dev/null +++ b/app/src/main/res/layout/item_search_suggestion_source_tip.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1300322d5..08495a58b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -663,4 +663,5 @@ Source unpinned Sources unpinned Sources pinned + Recent sources