Use FullComposeController for Source Feed
This commit is contained in:
parent
a760198981
commit
9dad9a6551
@ -1,5 +1,6 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -7,28 +8,39 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.presentation.util.bottomNavPaddingValues
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
@ -92,7 +104,6 @@ sealed class SourceFeedUI {
|
||||
|
||||
@Composable
|
||||
fun SourceFeedScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: SourceFeedPresenter,
|
||||
onClickBrowse: () -> Unit,
|
||||
onClickLatest: () -> Unit,
|
||||
@ -100,12 +111,29 @@ fun SourceFeedScreen(
|
||||
onClickDelete: (FeedSavedSearch) -> Unit,
|
||||
onClickManga: (Manga) -> Unit,
|
||||
) {
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
else -> {
|
||||
SourceFeedList(
|
||||
nestedScrollConnection = nestedScrollInterop,
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val insets = WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(insets)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
SourceFeedToolbar(
|
||||
title = presenter.source.name,
|
||||
state = presenter,
|
||||
scrollBehavior = scrollBehavior,
|
||||
incognitoMode = presenter.isIncognitoMode,
|
||||
downloadedOnlyMode = presenter.isDownloadOnly,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
Crossfade(targetState = presenter.isLoading) { state ->
|
||||
when (state) {
|
||||
true -> LoadingScreen()
|
||||
false -> {
|
||||
SourceFeedList(
|
||||
state = presenter,
|
||||
paddingValues = paddingValues,
|
||||
onClickBrowse = onClickBrowse,
|
||||
onClickLatest = onClickLatest,
|
||||
onClickSavedSearch = onClickSavedSearch,
|
||||
@ -115,11 +143,13 @@ fun SourceFeedScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourceFeedList(
|
||||
nestedScrollConnection: NestedScrollConnection,
|
||||
state: SourceFeedState,
|
||||
paddingValues: PaddingValues,
|
||||
onClickBrowse: () -> Unit,
|
||||
onClickLatest: () -> Unit,
|
||||
onClickSavedSearch: (SavedSearch) -> Unit,
|
||||
@ -127,8 +157,10 @@ fun SourceFeedList(
|
||||
onClickManga: (Manga) -> Unit,
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollConnection),
|
||||
contentPadding = bottomNavPaddingValues + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
contentPadding = paddingValues +
|
||||
bottomNavPaddingValues +
|
||||
WindowInsets.navigationBars.only(WindowInsetsSides.Vertical).asPaddingValues() +
|
||||
topPaddingValues,
|
||||
) {
|
||||
items(
|
||||
state.items.orEmpty(),
|
||||
@ -217,3 +249,35 @@ fun SourceFeedItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourceFeedToolbar(
|
||||
title: String,
|
||||
state: SourceFeedState,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
incognitoMode: Boolean,
|
||||
downloadedOnlyMode: Boolean,
|
||||
) {
|
||||
when {
|
||||
state.searchQuery != null -> SearchToolbar(
|
||||
searchQuery = state.searchQuery!!,
|
||||
onChangeSearchQuery = { state.searchQuery = it },
|
||||
onClickCloseSearch = { state.searchQuery = null },
|
||||
onClickResetSearch = { state.searchQuery = "" },
|
||||
scrollBehavior = scrollBehavior,
|
||||
incognitoMode = incognitoMode,
|
||||
downloadedOnlyMode = downloadedOnlyMode,
|
||||
)
|
||||
else -> AppBar(
|
||||
title = title,
|
||||
incognitoMode = incognitoMode,
|
||||
downloadedOnlyMode = downloadedOnlyMode,
|
||||
scrollBehavior = scrollBehavior,
|
||||
actions = {
|
||||
IconButton(onClick = { state.searchQuery = "" }) {
|
||||
Icon(Icons.Outlined.Search, contentDescription = stringResource(R.string.action_search))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import androidx.compose.runtime.setValue
|
||||
@Stable
|
||||
interface SourceFeedState {
|
||||
val isLoading: Boolean
|
||||
var searchQuery: String?
|
||||
val items: List<SourceFeedUI>?
|
||||
}
|
||||
|
||||
@ -17,5 +18,6 @@ fun SourceFeedState(): SourceFeedState {
|
||||
|
||||
class SourceFeedStateImpl : SourceFeedState {
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var searchQuery: String? by mutableStateOf(null)
|
||||
override var items: List<SourceFeedUI>? by mutableStateOf(null)
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.source.feed
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
@ -17,7 +14,7 @@ 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.SearchableComposeController
|
||||
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
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet
|
||||
@ -41,7 +38,7 @@ import xyz.nulldev.ts.api.http.serializer.FilterSerializer
|
||||
* [SourceFeedCardAdapter.OnMangaClickListener] called when manga is clicked in global search
|
||||
*/
|
||||
open class SourceFeedController :
|
||||
SearchableComposeController<SourceFeedPresenter>,
|
||||
FullComposeController<SourceFeedPresenter>,
|
||||
FabController {
|
||||
|
||||
constructor(source: CatalogueSource?) : super(
|
||||
@ -68,14 +65,6 @@ open class SourceFeedController :
|
||||
*/
|
||||
private var filterSheet: SourceFilterSheet? = null
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return source!!.name
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the [SourceFeedPresenter] used in controller.
|
||||
*
|
||||
@ -85,26 +74,6 @@ open class SourceFeedController :
|
||||
return SourceFeedPresenter(source = source!!)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds items to the options menu.
|
||||
*
|
||||
* @param menu menu containing options.
|
||||
* @param inflater used to load the menu xml.
|
||||
*/
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
createOptionsMenu(menu, inflater, R.menu.global_search, R.id.action_search)
|
||||
}
|
||||
|
||||
override fun onSearchViewQueryTextSubmit(query: String?) {
|
||||
onBrowseClick(query?.nullIfBlank())
|
||||
}
|
||||
|
||||
override fun onSearchViewQueryTextChange(newText: String?) {
|
||||
if (router.backstack.lastOrNull()?.controller == this) {
|
||||
presenter.query = newText ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the view is created
|
||||
*
|
||||
@ -136,11 +105,11 @@ open class SourceFeedController :
|
||||
filterSheet?.dismiss()
|
||||
if (allDefault) {
|
||||
onBrowseClick(
|
||||
presenter.query.nullIfBlank(),
|
||||
presenter.searchQuery?.nullIfBlank(),
|
||||
)
|
||||
} else {
|
||||
onBrowseClick(
|
||||
presenter.query.nullIfBlank(),
|
||||
presenter.searchQuery?.nullIfBlank(),
|
||||
filters = Json.encodeToString(filterSerializer.serialize(presenter.sourceFilters)),
|
||||
)
|
||||
}
|
||||
@ -173,7 +142,7 @@ open class SourceFeedController :
|
||||
|
||||
if (!allDefault) {
|
||||
onBrowseClick(
|
||||
search = presenter.query.nullIfBlank(),
|
||||
search = presenter.searchQuery?.nullIfBlank(),
|
||||
savedSearch = search.id,
|
||||
)
|
||||
}
|
||||
@ -228,9 +197,8 @@ open class SourceFeedController :
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
|
||||
override fun ComposeContent() {
|
||||
SourceFeedScreen(
|
||||
nestedScrollInterop = nestedScrollInterop,
|
||||
presenter = presenter,
|
||||
onClickBrowse = ::onBrowseClick,
|
||||
onClickLatest = ::onLatestClick,
|
||||
|
@ -1,6 +1,7 @@
|
||||
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
|
||||
@ -71,6 +72,9 @@ open class SourceFeedPresenter(
|
||||
private val getExhSavedSearch: GetExhSavedSearch = Injekt.get(),
|
||||
) : BasePresenter<SourceFeedController>(), SourceFeedState by state {
|
||||
|
||||
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState()
|
||||
val isIncognitoMode: Boolean by preferences.incognitoMode().asState()
|
||||
|
||||
/**
|
||||
* Fetches the different sources by user settings.
|
||||
*/
|
||||
@ -97,10 +101,6 @@ open class SourceFeedPresenter(
|
||||
|
||||
var filterItems: List<IFlexible<*>> = emptyList()
|
||||
|
||||
init {
|
||||
query = ""
|
||||
}
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user