mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Dedupe Global/MigrateSearchContent composables
This commit is contained in:
		@@ -24,6 +24,7 @@ import androidx.compose.runtime.State
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchEmptyResultItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
 | 
			
		||||
@@ -43,7 +44,6 @@ import tachiyomi.presentation.core.components.material.padding
 | 
			
		||||
@Composable
 | 
			
		||||
fun GlobalSearchScreen(
 | 
			
		||||
    state: GlobalSearchScreenModel.State,
 | 
			
		||||
    items: Map<CatalogueSource, SearchItemResult>,
 | 
			
		||||
    navigateUp: () -> Unit,
 | 
			
		||||
    onChangeSearchQuery: (String?) -> Unit,
 | 
			
		||||
    onSearch: (String) -> Unit,
 | 
			
		||||
@@ -129,7 +129,7 @@ fun GlobalSearchScreen(
 | 
			
		||||
        },
 | 
			
		||||
    ) { paddingValues ->
 | 
			
		||||
        GlobalSearchContent(
 | 
			
		||||
            items = items,
 | 
			
		||||
            items = state.filteredItems,
 | 
			
		||||
            contentPadding = paddingValues,
 | 
			
		||||
            getManga = getManga,
 | 
			
		||||
            onClickSource = onClickSource,
 | 
			
		||||
@@ -140,7 +140,8 @@ fun GlobalSearchScreen(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun GlobalSearchContent(
 | 
			
		||||
internal fun GlobalSearchContent(
 | 
			
		||||
    sourceId: Long? = null,
 | 
			
		||||
    items: Map<CatalogueSource, SearchItemResult>,
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    getManga: @Composable (Manga) -> State<Manga>,
 | 
			
		||||
@@ -154,7 +155,7 @@ private fun GlobalSearchContent(
 | 
			
		||||
        items.forEach { (source, result) ->
 | 
			
		||||
            item(key = source.id) {
 | 
			
		||||
                GlobalSearchResultItem(
 | 
			
		||||
                    title = source.name,
 | 
			
		||||
                    title = sourceId?.let { "▶ ${source.name}".takeIf { source.id == sourceId } } ?: source.name,
 | 
			
		||||
                    subtitle = LocaleHelper.getDisplayName(source.lang),
 | 
			
		||||
                    onClick = { onClickSource(source) },
 | 
			
		||||
                ) {
 | 
			
		||||
@@ -164,14 +165,7 @@ private fun GlobalSearchContent(
 | 
			
		||||
                        }
 | 
			
		||||
                        is SearchItemResult.Success -> {
 | 
			
		||||
                            if (result.isEmpty) {
 | 
			
		||||
                                Text(
 | 
			
		||||
                                    text = stringResource(R.string.no_results_found),
 | 
			
		||||
                                    modifier = Modifier
 | 
			
		||||
                                        .padding(
 | 
			
		||||
                                            horizontal = MaterialTheme.padding.medium,
 | 
			
		||||
                                            vertical = MaterialTheme.padding.small,
 | 
			
		||||
                                        ),
 | 
			
		||||
                                )
 | 
			
		||||
                                GlobalSearchEmptyResultItem()
 | 
			
		||||
                                return@GlobalSearchResultItem
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,17 @@
 | 
			
		||||
package eu.kanade.presentation.browse
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.lazy.LazyColumn
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.State
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchEmptyResultItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
 | 
			
		||||
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
 | 
			
		||||
import eu.kanade.tachiyomi.source.CatalogueSource
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchState
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreenModel
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga
 | 
			
		||||
import tachiyomi.presentation.core.components.material.Scaffold
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun MigrateSearchScreen(
 | 
			
		||||
    navigateUp: () -> Unit,
 | 
			
		||||
    state: MigrateSearchState,
 | 
			
		||||
    state: MigrateSearchScreenModel.State,
 | 
			
		||||
    getManga: @Composable (Manga) -> State<Manga>,
 | 
			
		||||
    onChangeSearchQuery: (String?) -> Unit,
 | 
			
		||||
    onSearch: (String) -> Unit,
 | 
			
		||||
@@ -41,8 +32,8 @@ fun MigrateSearchScreen(
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
    ) { paddingValues ->
 | 
			
		||||
        MigrateSearchContent(
 | 
			
		||||
            sourceId = state.manga?.source ?: -1,
 | 
			
		||||
        GlobalSearchContent(
 | 
			
		||||
            sourceId = state.manga?.source,
 | 
			
		||||
            items = state.items,
 | 
			
		||||
            contentPadding = paddingValues,
 | 
			
		||||
            getManga = getManga,
 | 
			
		||||
@@ -52,50 +43,3 @@ fun MigrateSearchScreen(
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun MigrateSearchContent(
 | 
			
		||||
    sourceId: Long,
 | 
			
		||||
    items: Map<CatalogueSource, SearchItemResult>,
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    getManga: @Composable (Manga) -> State<Manga>,
 | 
			
		||||
    onClickSource: (CatalogueSource) -> Unit,
 | 
			
		||||
    onClickItem: (Manga) -> Unit,
 | 
			
		||||
    onLongClickItem: (Manga) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    LazyColumn(
 | 
			
		||||
        contentPadding = contentPadding,
 | 
			
		||||
    ) {
 | 
			
		||||
        items.forEach { (source, result) ->
 | 
			
		||||
            item(key = source.id) {
 | 
			
		||||
                GlobalSearchResultItem(
 | 
			
		||||
                    title = if (source.id == sourceId) "▶ ${source.name}" else source.name,
 | 
			
		||||
                    subtitle = LocaleHelper.getDisplayName(source.lang),
 | 
			
		||||
                    onClick = { onClickSource(source) },
 | 
			
		||||
                ) {
 | 
			
		||||
                    when (result) {
 | 
			
		||||
                        SearchItemResult.Loading -> {
 | 
			
		||||
                            GlobalSearchLoadingResultItem()
 | 
			
		||||
                        }
 | 
			
		||||
                        is SearchItemResult.Success -> {
 | 
			
		||||
                            if (result.isEmpty) {
 | 
			
		||||
                                GlobalSearchEmptyResultItem()
 | 
			
		||||
                                return@GlobalSearchResultItem
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            GlobalSearchCardRow(
 | 
			
		||||
                                titles = result.result,
 | 
			
		||||
                                getManga = getManga,
 | 
			
		||||
                                onClick = onClickItem,
 | 
			
		||||
                                onLongClick = onLongClickItem,
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                        is SearchItemResult.Error -> {
 | 
			
		||||
                            GlobalSearchErrorResultItem(message = result.throwable.message)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,13 @@ import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
class MigrateSearchScreenModel(
 | 
			
		||||
    val mangaId: Long,
 | 
			
		||||
    initialExtensionFilter: String = "",
 | 
			
		||||
    preferences: BasePreferences = Injekt.get(),
 | 
			
		||||
    private val sourcePreferences: SourcePreferences = Injekt.get(),
 | 
			
		||||
    private val sourceManager: SourceManager = Injekt.get(),
 | 
			
		||||
    private val getManga: GetManga = Injekt.get(),
 | 
			
		||||
) : SearchScreenModel<MigrateSearchState>(MigrateSearchState()) {
 | 
			
		||||
) : SearchScreenModel<MigrateSearchScreenModel.State>(State()) {
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        extensionFilter = initialExtensionFilter
 | 
			
		||||
        coroutineScope.launch {
 | 
			
		||||
            val manga = getManga.await(mangaId)!!
 | 
			
		||||
 | 
			
		||||
@@ -73,21 +71,19 @@ class MigrateSearchScreenModel(
 | 
			
		||||
            it.copy(dialog = dialog)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val manga: Manga? = null,
 | 
			
		||||
        val searchQuery: String? = null,
 | 
			
		||||
        val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
 | 
			
		||||
        val dialog: MigrateSearchDialog? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
        val progress: Int = items.count { it.value !is SearchItemResult.Loading }
 | 
			
		||||
        val total: Int = items.size
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sealed class MigrateSearchDialog {
 | 
			
		||||
    data class Migrate(val manga: Manga) : MigrateSearchDialog()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Immutable
 | 
			
		||||
data class MigrateSearchState(
 | 
			
		||||
    val manga: Manga? = null,
 | 
			
		||||
    val searchQuery: String? = null,
 | 
			
		||||
    val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
 | 
			
		||||
    val dialog: MigrateSearchDialog? = null,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    val progress: Int = items.count { it.value !is SearchItemResult.Loading }
 | 
			
		||||
 | 
			
		||||
    val total: Int = items.size
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ import tachiyomi.presentation.core.screens.LoadingScreen
 | 
			
		||||
 | 
			
		||||
class GlobalSearchScreen(
 | 
			
		||||
    val searchQuery: String = "",
 | 
			
		||||
    private val extensionFilter: String = "",
 | 
			
		||||
    private val extensionFilter: String? = null,
 | 
			
		||||
) : Screen() {
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
@@ -33,9 +33,8 @@ class GlobalSearchScreen(
 | 
			
		||||
        }
 | 
			
		||||
        val state by screenModel.state.collectAsState()
 | 
			
		||||
        var showSingleLoadingScreen by remember {
 | 
			
		||||
            mutableStateOf(searchQuery.isNotEmpty() && extensionFilter.isNotEmpty() && state.total == 1)
 | 
			
		||||
            mutableStateOf(searchQuery.isNotEmpty() && !extensionFilter.isNullOrEmpty() && state.total == 1)
 | 
			
		||||
        }
 | 
			
		||||
        val filteredSources by screenModel.searchPagerFlow.collectAsState()
 | 
			
		||||
 | 
			
		||||
        if (showSingleLoadingScreen) {
 | 
			
		||||
            LoadingScreen()
 | 
			
		||||
@@ -58,7 +57,6 @@ class GlobalSearchScreen(
 | 
			
		||||
        } else {
 | 
			
		||||
            GlobalSearchScreen(
 | 
			
		||||
                state = state,
 | 
			
		||||
                items = filteredSources,
 | 
			
		||||
                navigateUp = navigator::pop,
 | 
			
		||||
                onChangeSearchQuery = screenModel::updateSearchQuery,
 | 
			
		||||
                onSearch = screenModel::search,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,7 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import eu.kanade.domain.base.BasePreferences
 | 
			
		||||
import eu.kanade.domain.source.service.SourcePreferences
 | 
			
		||||
import eu.kanade.presentation.util.ioCoroutineScope
 | 
			
		||||
import eu.kanade.tachiyomi.source.CatalogueSource
 | 
			
		||||
import kotlinx.coroutines.flow.SharingStarted
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.map
 | 
			
		||||
import kotlinx.coroutines.flow.stateIn
 | 
			
		||||
import kotlinx.coroutines.flow.update
 | 
			
		||||
import tachiyomi.domain.source.service.SourceManager
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
@@ -16,7 +11,7 @@ import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
class GlobalSearchScreenModel(
 | 
			
		||||
    initialQuery: String = "",
 | 
			
		||||
    initialExtensionFilter: String = "",
 | 
			
		||||
    initialExtensionFilter: String? = null,
 | 
			
		||||
    preferences: BasePreferences = Injekt.get(),
 | 
			
		||||
    private val sourcePreferences: SourcePreferences = Injekt.get(),
 | 
			
		||||
    private val sourceManager: SourceManager = Injekt.get(),
 | 
			
		||||
@@ -24,17 +19,9 @@ class GlobalSearchScreenModel(
 | 
			
		||||
 | 
			
		||||
    val incognitoMode = preferences.incognitoMode()
 | 
			
		||||
    val lastUsedSourceId = sourcePreferences.lastUsedSource()
 | 
			
		||||
 | 
			
		||||
    val searchPagerFlow = state.map { Pair(it.onlyShowHasResults, it.items) }
 | 
			
		||||
        .distinctUntilChanged()
 | 
			
		||||
        .map { (onlyShowHasResults, items) ->
 | 
			
		||||
            items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
 | 
			
		||||
        }
 | 
			
		||||
        .stateIn(ioCoroutineScope, SharingStarted.Lazily, state.value.items)
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        extensionFilter = initialExtensionFilter
 | 
			
		||||
        if (initialQuery.isNotBlank() || initialExtensionFilter.isNotBlank()) {
 | 
			
		||||
        if (initialQuery.isNotBlank() || !initialExtensionFilter.isNullOrBlank()) {
 | 
			
		||||
            search(initialQuery)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -77,10 +64,6 @@ class GlobalSearchScreenModel(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun SearchItemResult.isVisible(onlyShowHasResults: Boolean): Boolean {
 | 
			
		||||
        return !onlyShowHasResults || (this is SearchItemResult.Success && !this.isEmpty)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
    data class State(
 | 
			
		||||
        val searchQuery: String? = null,
 | 
			
		||||
@@ -90,5 +73,10 @@ class GlobalSearchScreenModel(
 | 
			
		||||
    ) {
 | 
			
		||||
        val progress: Int = items.count { it.value !is SearchItemResult.Loading }
 | 
			
		||||
        val total: Int = items.size
 | 
			
		||||
        val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun SearchItemResult.isVisible(onlyShowHasResults: Boolean): Boolean {
 | 
			
		||||
    return !onlyShowHasResults || (this is SearchItemResult.Success && !this.isEmpty)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ abstract class SearchScreenModel<T>(
 | 
			
		||||
    private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher()
 | 
			
		||||
 | 
			
		||||
    protected var query: String? = null
 | 
			
		||||
    protected lateinit var extensionFilter: String
 | 
			
		||||
    protected var extensionFilter: String? = null
 | 
			
		||||
 | 
			
		||||
    private val sources by lazy { getSelectedSources() }
 | 
			
		||||
    private val pinnedSources by lazy { sourcePreferences.pinnedSources().get() }
 | 
			
		||||
@@ -63,11 +63,10 @@ abstract class SearchScreenModel<T>(
 | 
			
		||||
    abstract fun getEnabledSources(): List<CatalogueSource>
 | 
			
		||||
 | 
			
		||||
    private fun getSelectedSources(): List<CatalogueSource> {
 | 
			
		||||
        val filter = extensionFilter
 | 
			
		||||
 | 
			
		||||
        val enabledSources = getEnabledSources()
 | 
			
		||||
 | 
			
		||||
        if (filter.isEmpty()) {
 | 
			
		||||
        val filter = extensionFilter
 | 
			
		||||
        if (filter.isNullOrEmpty()) {
 | 
			
		||||
            return enabledSources
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -416,7 +416,7 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
            INTENT_SEARCH -> {
 | 
			
		||||
                val query = intent.getStringExtra(INTENT_SEARCH_QUERY)
 | 
			
		||||
                if (!query.isNullOrEmpty()) {
 | 
			
		||||
                    val filter = intent.getStringExtra(INTENT_SEARCH_FILTER) ?: ""
 | 
			
		||||
                    val filter = intent.getStringExtra(INTENT_SEARCH_FILTER)
 | 
			
		||||
                    navigator.popUntilRoot()
 | 
			
		||||
                    navigator.push(GlobalSearchScreen(query, filter))
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user