diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt index d4a504ae5..7fd97222a 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt @@ -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,26 +111,45 @@ 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, - onClickBrowse = onClickBrowse, - onClickLatest = onClickLatest, - onClickSavedSearch = onClickSavedSearch, - onClickDelete = onClickDelete, - onClickManga = onClickManga, + 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, + onClickDelete = onClickDelete, + onClickManga = onClickManga, + ) + } + } } } } @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)) + } + }, + ) + } +} diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedState.kt b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedState.kt index 13de0ae68..7d93eafa3 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedState.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedState.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.setValue @Stable interface SourceFeedState { val isLoading: Boolean + var searchQuery: String? val items: List? } @@ -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? by mutableStateOf(null) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedController.kt index a2a6f907b..dbdad8768 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedController.kt @@ -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, + FullComposeController, 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, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt index 8de11ebfe..dc22a3a9f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt @@ -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(), 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> = emptyList() - init { - query = "" - } - override fun onCreate(savedState: Bundle?) { super.onCreate(savedState)