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

View File

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

View File

@ -4,9 +4,7 @@ import android.os.Bundle
import android.view.View
import androidx.compose.runtime.Composable
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
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.source.interactor.GetRemoteManga
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.SourceManager
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.withFadeTransaction
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
*/
open class SourceFeedController :
FullComposeController<SourceFeedPresenter>,
FabController {
FullComposeController<SourceFeedPresenter> {
constructor(source: CatalogueSource?) : super(
bundleOf(
@ -58,8 +54,6 @@ open class SourceFeedController :
var source: CatalogueSource? = null
private var actionFab: ExtendedFloatingActionButton? = null
/**
* Sheet containing filter items.
*/
@ -89,10 +83,6 @@ open class SourceFeedController :
private val filterSerializer = FilterSerializer()
fun initFilterSheet() {
if (presenter.sourceFilters.isEmpty()) {
actionFab?.text = activity!!.getString(R.string.saved_searches)
}
filterSheet = SourceFilterSheet(
activity!!,
// SY -->
@ -101,7 +91,7 @@ open class SourceFeedController :
emptyList(),
// SY <--
onFilterClicked = {
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
val allDefault = presenter.filters == presenter.source.getFilterList()
filterSheet?.dismiss()
if (allDefault) {
onBrowseClick(
@ -110,7 +100,7 @@ open class SourceFeedController :
} else {
onBrowseClick(
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
}
presenter.sourceFilters = FilterList(search.filterList)
presenter.setFilters(FilterList(search.filterList))
filterSheet?.setFilters(presenter.filterItems)
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
val allDefault = presenter.filters == presenter.source.getFilterList()
filterSheet?.dismiss()
if (!allDefault) {
@ -171,35 +161,13 @@ open class SourceFeedController :
filterSheet?.setSavedSearches(presenter.loadSearches())
}
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
override fun ComposeContent() {
SourceFeedScreen(
presenter = presenter,
onFabClick = { filterSheet?.show() },
onClickBrowse = ::onBrowseClick,
onClickLatest = ::onLatestClick,
onClickSavedSearch = ::onSavedSearchClick,

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.browse.source.feed
import android.os.Bundle
import androidx.compose.runtime.getValue
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.InsertManga
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.GetExhSavedSearch
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.InsertFeedSavedSearch
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.SManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.toItems
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchNonCancellableIO
import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.system.logcat
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.
*
* @param source the source.
* @param handler manages the database calls.
* @param preferences manages the preference calls.
*/
open class SourceFeedPresenter(
private val state: SourceFeedStateImpl = SourceFeedState() as SourceFeedStateImpl,
@ -66,7 +61,6 @@ open class SourceFeedPresenter(
private val getFeedSavedSearchBySourceId: GetFeedSavedSearchBySourceId = Injekt.get(),
private val getSavedSearchBySourceIdFeed: GetSavedSearchBySourceIdFeed = Injekt.get(),
private val countFeedSavedSearchBySourceId: CountFeedSavedSearchBySourceId = Injekt.get(),
private val getSavedSearchBySourceId: GetSavedSearchBySourceId = Injekt.get(),
private val insertFeedSavedSearch: InsertFeedSavedSearch = Injekt.get(),
private val deleteFeedSavedSearchById: DeleteFeedSavedSearchById = Injekt.get(),
private val getExhSavedSearch: GetExhSavedSearch = Injekt.get(),
@ -90,21 +84,10 @@ open class SourceFeedPresenter(
*/
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?) {
super.onCreate(savedState)
sourceFilters = source.getFilterList()
setFilters(source.getFilterList())
getFeedSavedSearchBySourceId.subscribe(source.id)
.onEach {
@ -122,16 +105,16 @@ open class SourceFeedPresenter(
super.onDestroy()
}
fun setFilters(filters: FilterList) {
state.filters = filters
}
suspend fun hasTooManyFeeds(): Boolean {
return countFeedSavedSearchBySourceId.await(source.id) > 10
}
suspend fun getSourceSavedSearches(): List<SavedSearch> {
return getSavedSearchBySourceId.await(source.id)
}
fun createFeed(savedSearchId: Long) {
launchIO {
presenterScope.launchNonCancellableIO {
insertFeedSavedSearch.await(
FeedSavedSearch(
id = -1,
@ -144,7 +127,7 @@ open class SourceFeedPresenter(
}
fun deleteFeed(feed: FeedSavedSearch) {
launchIO {
presenterScope.launchNonCancellableIO {
deleteFeedSavedSearchById.await(feed.id)
}
}

View File

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