Control FAB with Compose in SourceFeed

This commit is contained in:
Jobobby04 2022-09-10 12:25:30 -04:00
parent e6767b747b
commit d0518515e9
5 changed files with 30 additions and 64 deletions

View File

@ -101,6 +101,7 @@ sealed class SourceFeedUI {
@Composable @Composable
fun SourceFeedScreen( fun SourceFeedScreen(
presenter: SourceFeedPresenter, presenter: SourceFeedPresenter,
onFabClick: () -> Unit,
onClickBrowse: () -> Unit, onClickBrowse: () -> Unit,
onClickLatest: () -> Unit, onClickLatest: () -> Unit,
onClickSavedSearch: (SavedSearch) -> Unit, onClickSavedSearch: (SavedSearch) -> Unit,
@ -117,6 +118,12 @@ fun SourceFeedScreen(
downloadedOnlyMode = presenter.isDownloadOnly, downloadedOnlyMode = presenter.isDownloadOnly,
) )
}, },
floatingActionButton = {
BrowseSourceFloatingActionButton(
isVisible = presenter.filterItems.isNotEmpty(),
onFabClick = onFabClick,
)
},
) { paddingValues -> ) { paddingValues ->
Crossfade(targetState = presenter.isLoading) { state -> Crossfade(targetState = presenter.isLoading) { state ->
when (state) { when (state) {

View File

@ -1,14 +1,20 @@
package eu.kanade.presentation.browse package eu.kanade.presentation.browse
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.ui.browse.source.browse.toItems
@Stable @Stable
interface SourceFeedState { interface SourceFeedState {
val isLoading: Boolean val isLoading: Boolean
var searchQuery: String? var searchQuery: String?
val filters: FilterList
val filterItems: List<IFlexible<*>>
val items: List<SourceFeedUI>? val items: List<SourceFeedUI>?
} }
@ -19,5 +25,7 @@ fun SourceFeedState(): SourceFeedState {
class SourceFeedStateImpl : SourceFeedState { class SourceFeedStateImpl : SourceFeedState {
override var isLoading: Boolean by mutableStateOf(true) override var isLoading: Boolean by mutableStateOf(true)
override var searchQuery: String? by mutableStateOf(null) override var searchQuery: String? by mutableStateOf(null)
override var filters: FilterList by mutableStateOf(FilterList())
override val filterItems: List<IFlexible<*>> by derivedStateOf { filters.toItems() }
override var items: List<SourceFeedUI>? by mutableStateOf(null) override var items: List<SourceFeedUI>? by mutableStateOf(null)
} }

View File

@ -4,9 +4,7 @@ import android.os.Bundle
import android.view.View import android.view.View
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.source.interactor.GetRemoteManga import eu.kanade.domain.source.interactor.GetRemoteManga
import eu.kanade.presentation.browse.SourceFeedScreen import eu.kanade.presentation.browse.SourceFeedScreen
@ -14,7 +12,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.ui.base.controller.FabController
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
@ -38,8 +35,7 @@ import xyz.nulldev.ts.api.http.serializer.FilterSerializer
* [SourceFeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search * [SourceFeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
*/ */
open class SourceFeedController : open class SourceFeedController :
FullComposeController<SourceFeedPresenter>, FullComposeController<SourceFeedPresenter> {
FabController {
constructor(source: CatalogueSource?) : super( constructor(source: CatalogueSource?) : super(
bundleOf( bundleOf(
@ -58,8 +54,6 @@ open class SourceFeedController :
var source: CatalogueSource? = null var source: CatalogueSource? = null
private var actionFab: ExtendedFloatingActionButton? = null
/** /**
* Sheet containing filter items. * Sheet containing filter items.
*/ */
@ -89,10 +83,6 @@ open class SourceFeedController :
private val filterSerializer = FilterSerializer() private val filterSerializer = FilterSerializer()
fun initFilterSheet() { fun initFilterSheet() {
if (presenter.sourceFilters.isEmpty()) {
actionFab?.text = activity!!.getString(R.string.saved_searches)
}
filterSheet = SourceFilterSheet( filterSheet = SourceFilterSheet(
activity!!, activity!!,
// SY --> // SY -->
@ -101,7 +91,7 @@ open class SourceFeedController :
emptyList(), emptyList(),
// SY <-- // SY <--
onFilterClicked = { onFilterClicked = {
val allDefault = presenter.sourceFilters == presenter.source.getFilterList() val allDefault = presenter.filters == presenter.source.getFilterList()
filterSheet?.dismiss() filterSheet?.dismiss()
if (allDefault) { if (allDefault) {
onBrowseClick( onBrowseClick(
@ -110,7 +100,7 @@ open class SourceFeedController :
} else { } else {
onBrowseClick( onBrowseClick(
presenter.searchQuery?.nullIfBlank(), presenter.searchQuery?.nullIfBlank(),
filters = Json.encodeToString(filterSerializer.serialize(presenter.sourceFilters)), filters = Json.encodeToString(filterSerializer.serialize(presenter.filters)),
) )
} }
}, },
@ -135,9 +125,9 @@ open class SourceFeedController :
return@launchUI return@launchUI
} }
presenter.sourceFilters = FilterList(search.filterList) presenter.setFilters(FilterList(search.filterList))
filterSheet?.setFilters(presenter.filterItems) filterSheet?.setFilters(presenter.filterItems)
val allDefault = presenter.sourceFilters == presenter.source.getFilterList() val allDefault = presenter.filters == presenter.source.getFilterList()
filterSheet?.dismiss() filterSheet?.dismiss()
if (!allDefault) { if (!allDefault) {
@ -171,35 +161,13 @@ open class SourceFeedController :
filterSheet?.setSavedSearches(presenter.loadSearches()) filterSheet?.setSavedSearches(presenter.loadSearches())
} }
filterSheet?.setFilters(presenter.filterItems) filterSheet?.setFilters(presenter.filterItems)
// TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly
filterSheet?.setOnShowListener { actionFab?.isVisible = false }
filterSheet?.setOnDismissListener { actionFab?.isVisible = true }
actionFab?.setOnClickListener { filterSheet?.show() }
actionFab?.isVisible = true
}
override fun configureFab(fab: ExtendedFloatingActionButton) {
actionFab = fab
// Controlled by initFilterSheet()
fab.isVisible = false
fab.setText(R.string.action_filter)
fab.setIconResource(R.drawable.ic_filter_list_24dp)
}
override fun cleanupFab(fab: ExtendedFloatingActionButton) {
fab.setOnClickListener(null)
actionFab = null
} }
@Composable @Composable
override fun ComposeContent() { override fun ComposeContent() {
SourceFeedScreen( SourceFeedScreen(
presenter = presenter, presenter = presenter,
onFabClick = { filterSheet?.show() },
onClickBrowse = ::onBrowseClick, onClickBrowse = ::onBrowseClick,
onClickLatest = ::onLatestClick, onClickLatest = ::onLatestClick,
onClickSavedSearch = ::onSavedSearchClick, onClickSavedSearch = ::onSavedSearchClick,

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.browse.source.feed
import android.os.Bundle import android.os.Bundle
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.InsertManga import eu.kanade.domain.manga.interactor.InsertManga
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
@ -12,7 +11,6 @@ import eu.kanade.domain.source.interactor.CountFeedSavedSearchBySourceId
import eu.kanade.domain.source.interactor.DeleteFeedSavedSearchById import eu.kanade.domain.source.interactor.DeleteFeedSavedSearchById
import eu.kanade.domain.source.interactor.GetExhSavedSearch import eu.kanade.domain.source.interactor.GetExhSavedSearch
import eu.kanade.domain.source.interactor.GetFeedSavedSearchBySourceId import eu.kanade.domain.source.interactor.GetFeedSavedSearchBySourceId
import eu.kanade.domain.source.interactor.GetSavedSearchBySourceId
import eu.kanade.domain.source.interactor.GetSavedSearchBySourceIdFeed import eu.kanade.domain.source.interactor.GetSavedSearchBySourceIdFeed
import eu.kanade.domain.source.interactor.InsertFeedSavedSearch import eu.kanade.domain.source.interactor.InsertFeedSavedSearch
import eu.kanade.presentation.browse.SourceFeedState import eu.kanade.presentation.browse.SourceFeedState
@ -27,8 +25,7 @@ import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.toItems import eu.kanade.tachiyomi.util.lang.launchNonCancellableIO
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.savedsearches.models.FeedSavedSearch import exh.savedsearches.models.FeedSavedSearch
@ -53,8 +50,6 @@ import xyz.nulldev.ts.api.http.serializer.FilterSerializer
* Function calls should be done from here. UI calls should be done from the controller. * Function calls should be done from here. UI calls should be done from the controller.
* *
* @param source the source. * @param source the source.
* @param handler manages the database calls.
* @param preferences manages the preference calls.
*/ */
open class SourceFeedPresenter( open class SourceFeedPresenter(
private val state: SourceFeedStateImpl = SourceFeedState() as SourceFeedStateImpl, private val state: SourceFeedStateImpl = SourceFeedState() as SourceFeedStateImpl,
@ -66,7 +61,6 @@ open class SourceFeedPresenter(
private val getFeedSavedSearchBySourceId: GetFeedSavedSearchBySourceId = Injekt.get(), private val getFeedSavedSearchBySourceId: GetFeedSavedSearchBySourceId = Injekt.get(),
private val getSavedSearchBySourceIdFeed: GetSavedSearchBySourceIdFeed = Injekt.get(), private val getSavedSearchBySourceIdFeed: GetSavedSearchBySourceIdFeed = Injekt.get(),
private val countFeedSavedSearchBySourceId: CountFeedSavedSearchBySourceId = Injekt.get(), private val countFeedSavedSearchBySourceId: CountFeedSavedSearchBySourceId = Injekt.get(),
private val getSavedSearchBySourceId: GetSavedSearchBySourceId = Injekt.get(),
private val insertFeedSavedSearch: InsertFeedSavedSearch = Injekt.get(), private val insertFeedSavedSearch: InsertFeedSavedSearch = Injekt.get(),
private val deleteFeedSavedSearchById: DeleteFeedSavedSearchById = Injekt.get(), private val deleteFeedSavedSearchById: DeleteFeedSavedSearchById = Injekt.get(),
private val getExhSavedSearch: GetExhSavedSearch = Injekt.get(), private val getExhSavedSearch: GetExhSavedSearch = Injekt.get(),
@ -90,21 +84,10 @@ open class SourceFeedPresenter(
*/ */
private var fetchImageSubscription: Subscription? = null private var fetchImageSubscription: Subscription? = null
/**
* Modifiable list of filters.
*/
var sourceFilters = FilterList()
set(value) {
field = value
filterItems = value.toItems()
}
var filterItems: List<IFlexible<*>> = emptyList()
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
sourceFilters = source.getFilterList() setFilters(source.getFilterList())
getFeedSavedSearchBySourceId.subscribe(source.id) getFeedSavedSearchBySourceId.subscribe(source.id)
.onEach { .onEach {
@ -122,16 +105,16 @@ open class SourceFeedPresenter(
super.onDestroy() super.onDestroy()
} }
fun setFilters(filters: FilterList) {
state.filters = filters
}
suspend fun hasTooManyFeeds(): Boolean { suspend fun hasTooManyFeeds(): Boolean {
return countFeedSavedSearchBySourceId.await(source.id) > 10 return countFeedSavedSearchBySourceId.await(source.id) > 10
} }
suspend fun getSourceSavedSearches(): List<SavedSearch> {
return getSavedSearchBySourceId.await(source.id)
}
fun createFeed(savedSearchId: Long) { fun createFeed(savedSearchId: Long) {
launchIO { presenterScope.launchNonCancellableIO {
insertFeedSavedSearch.await( insertFeedSavedSearch.await(
FeedSavedSearch( FeedSavedSearch(
id = -1, id = -1,
@ -144,7 +127,7 @@ open class SourceFeedPresenter(
} }
fun deleteFeed(feed: FeedSavedSearch) { fun deleteFeed(feed: FeedSavedSearch) {
launchIO { presenterScope.launchNonCancellableIO {
deleteFeedSavedSearchById.await(feed.id) deleteFeedSavedSearchById.await(feed.id)
} }
} }

View File

@ -85,7 +85,7 @@ open class AutoComplete(val filter: Filter.AutoComplete) : AbstractFlexibleItem<
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false
return filter == (other as SelectItem).filter return filter == (other as AutoComplete).filter
} }
override fun hashCode(): Int { override fun hashCode(): Int {