Use FullComposeController for Source Feed

This commit is contained in:
Jobobby04 2022-08-31 16:48:31 -04:00
parent a760198981
commit 9dad9a6551
4 changed files with 91 additions and 57 deletions

View File

@ -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,
@ -114,12 +142,14 @@ 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))
}
},
)
}
}

View File

@ -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)
}

View File

@ -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,

View File

@ -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)