Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
Expand Down Expand Up @@ -44,7 +43,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
Expand Down Expand Up @@ -90,7 +88,6 @@ fun GalleryList(
detailItemContent: @Composable (LazyGridItemScope.(BaseGalleryInfo) -> Unit),
thumbListState: LazyStaggeredGridState = rememberLazyStaggeredGridState(),
thumbItemContent: @Composable (LazyStaggeredGridItemScope.(BaseGalleryInfo) -> Unit),
searchBarOffsetY: () -> Int,
scrollToTopOnRefresh: Boolean = true,
onRefresh: () -> Unit,
onLoading: () -> Unit,
Expand Down Expand Up @@ -220,12 +217,10 @@ fun GalleryList(
ErrorTip(modifier = Modifier.widthIn(max = 228.dp), text = stringResource(id = R.string.gallery_list_empty_hit))
}
}

PullToRefreshDefaults.LoadingIndicator(
state = refreshState,
isRefreshing = isRefreshing,
modifier = Modifier.align(Alignment.TopCenter).padding(top = contentPadding.calculateTopPadding())
.offset { IntOffset(0, searchBarOffsetY()) },
modifier = Modifier.align(Alignment.TopCenter).padding(top = contentPadding.calculateTopPadding()),
)
}
}
Expand Down
53 changes: 21 additions & 32 deletions app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.SwipeToDismissBoxDefaults
import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.fork.SwipeToDismissBox
import androidx.compose.material3.fork.SwipeToDismissBoxState
import androidx.compose.material3.rememberSearchBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
Expand All @@ -76,11 +78,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -135,7 +133,6 @@ import com.hippo.ehviewer.ui.tools.awaitSingleChoice
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlin.math.roundToInt
import kotlinx.coroutines.delay
import moe.tarsin.navigate
import sh.calvin.reorderable.ReorderableItem
Expand All @@ -151,17 +148,16 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
var filterState by rememberSerializable { mutableStateOf(DownloadsFilterState(filterMode, Settings.recentDownloadLabel.value)) }
var invalidateKey by rememberSaveable { mutableStateOf(false) }
var isLoading by rememberSaveable { mutableStateOf(true) }
var searchBarExpanded by rememberSaveable { mutableStateOf(false) }
var searchBarOffsetY by remember { mutableIntStateOf(0) }
val searchBarState = rememberSearchBarState()
val scrollBehavior = SearchBarDefaults.enterAlwaysSearchBarScrollBehavior()
val animateItems by Settings.animateItems.collectAsState()

var fabExpanded by remember { mutableStateOf(false) }
var fabHidden by remember { mutableStateOf(false) }
val checkedInfoMap = remember { mutableStateMapOf<Long, DownloadInfo>() }
val selectMode by rememberUpdatedState(checkedInfoMap.isNotEmpty())
DrawerHandle(!selectMode && !searchBarExpanded)
DrawerHandle(!selectMode && !searchBarState.expanded)

val density = LocalDensity.current
val canTranslate = Settings.showTagTranslations.value && EhTagDatabase.translatable && EhTagDatabase.initialized
val ehTags = EhTagDatabase.takeIf { canTranslate }
fun getTranslation(tag: String) = ehTags?.run {
Expand Down Expand Up @@ -234,7 +230,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
}

LaunchedEffect(filterState) {
searchBarOffsetY = 0
scrollBehavior.reset()
}

ProvideSideSheetContent { drawerState ->
Expand Down Expand Up @@ -471,18 +467,18 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
override val keyword = LABEL_PREFIX + label
override fun onClick() {
searchFieldState.setTextAndPlaceCursorAtEnd(keyword)
searchBarExpanded = false
launch { searchBarState.animateToCollapsed() }
switchLabel(label)
}
}
searchBarState.CollectExpanded {
fabHidden = it
if (it) checkedInfoMap.clear()
}
SearchBarScreen(
onApplySearch = { filterState = filterState.copy(keyword = it) },
expanded = searchBarExpanded,
onExpandedChange = {
searchBarExpanded = it
fabHidden = it
if (it) checkedInfoMap.clear()
},
searchBarState = searchBarState,
scrollBehavior = scrollBehavior,
title = title,
searchFieldHint = hint,
searchFieldState = searchFieldState,
Expand All @@ -494,7 +490,6 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
EhDB.searchDownloadLabel(label, 10).map(::DownloadLabelSuggestion)
}
},
searchBarOffsetY = { searchBarOffsetY },
trailingIcon = {
var expanded by remember { mutableStateOf(false) }
val sideSheetState = LocalSideSheetState.current
Expand Down Expand Up @@ -557,19 +552,13 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
) { contentPadding ->
val height by collectListThumbSizeAsState()
val realPadding = contentPadding + PaddingValues(dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_h), dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_v))
val searchBarConnection = remember {
val connection = remember {
val slop = ViewConfiguration.get(contextOf<Context>()).scaledTouchSlop
val topPaddingPx = with(density) { contentPadding.calculateTopPadding().roundToPx() }
object : NestedScrollConnection {
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
val dy = -consumed.y
if (dy >= slop) {
fabHidden = true
} else if (dy <= -slop / 2) {
fabHidden = false
}
searchBarOffsetY = (searchBarOffsetY - dy).roundToInt().coerceIn(-topPaddingPx, 0)
return Offset.Zero // We never consume it
scrollBehavior.nestedScrollConnection.watchPostScroll { (_, y) ->
if (-y >= slop) {
fabHidden = true
} else if (-y <= -slop / 2) {
fabHidden = false
}
}
}
Expand All @@ -585,7 +574,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
val thumbColumns by Settings.thumbColumns.collectAsState()
FastScrollLazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(thumbColumns),
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
modifier = Modifier.nestedScroll(connection).fillMaxSize(),
contentPadding = realPadding,
verticalItemSpacing = gridInterval,
horizontalArrangement = Arrangement.spacedBy(gridInterval),
Expand All @@ -603,7 +592,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
}
} else {
FastScrollLazyColumn(
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
modifier = Modifier.nestedScroll(connection).fillMaxSize(),
contentPadding = realPadding,
verticalArrangement = Arrangement.spacedBy(dimensionResource(com.hippo.ehviewer.R.dimen.gallery_list_interval)),
) {
Expand Down
55 changes: 21 additions & 34 deletions app/src/main/kotlin/com/hippo/ehviewer/ui/screen/FavoritesScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,22 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.rememberSearchBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.currentRecomposeScope
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
Expand Down Expand Up @@ -86,7 +82,6 @@ import com.hippo.ehviewer.ui.tools.awaitSelectItem
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlin.math.roundToInt
import kotlinx.coroutines.delay
import moe.tarsin.coroutines.runSwallowingWithUI
import moe.tarsin.navigate
Expand All @@ -102,8 +97,8 @@ fun AnimatedVisibilityScope.FavouritesScreen(navigator: DestinationsNavigator, v

// Meta State
var urlBuilder by viewModel.urlBuilder
var searchBarExpanded by rememberSaveable { mutableStateOf(false) }
var searchBarOffsetY by remember { mutableIntStateOf(0) }
val searchBarState = rememberSearchBarState()
val scrollBehavior = SearchBarDefaults.enterAlwaysSearchBarScrollBehavior()
var fabExpanded by remember { mutableStateOf(false) }
var fabHidden by remember { mutableStateOf(false) }

Expand All @@ -112,7 +107,7 @@ fun AnimatedVisibilityScope.FavouritesScreen(navigator: DestinationsNavigator, v
val favCatName = remember(urlBuilder) {
when (val favCat = urlBuilder.favCat) {
in 0..9 -> Settings.favCat[favCat]
FavListUrlBuilder.FAV_CAT_LOCAL -> localFavName.also { searchBarOffsetY = 0 }
FavListUrlBuilder.FAV_CAT_LOCAL -> localFavName.also { scrollBehavior.reset() }
else -> cloudFavName
}
}
Expand All @@ -121,7 +116,6 @@ fun AnimatedVisibilityScope.FavouritesScreen(navigator: DestinationsNavigator, v
} else {
stringResource(R.string.favorites_title_2, favCatName, keyword)
}
val density = LocalDensity.current
val searchBarHint = stringResource(R.string.search_bar_hint, favCatName)
val data = viewModel.data.collectAsLazyPagingItems()

Expand Down Expand Up @@ -176,20 +170,20 @@ fun AnimatedVisibilityScope.FavouritesScreen(navigator: DestinationsNavigator, v

val checkedInfoMap = remember { mutableStateMapOf<Long, BaseGalleryInfo>() }
val selectMode = checkedInfoMap.isNotEmpty()
DrawerHandle(!selectMode && !searchBarExpanded)
DrawerHandle(!selectMode && !searchBarState.expanded)

searchBarState.CollectExpanded {
fabHidden = it
if (it) checkedInfoMap.clear()
}

SearchBarScreen(
onApplySearch = { refresh(FavListUrlBuilder(urlBuilder.favCat, it)) },
expanded = searchBarExpanded,
onExpandedChange = {
searchBarExpanded = it
fabHidden = it
if (it) checkedInfoMap.clear()
},
searchBarState = searchBarState,
scrollBehavior = scrollBehavior,
title = title,
searchFieldHint = searchBarHint,
localSearch = urlBuilder.isLocal,
searchBarOffsetY = { searchBarOffsetY },
trailingIcon = {
val sheetState = LocalSideSheetState.current
IconButton(onClick = { launch { sheetState.open() } }, shapes = IconButtonDefaults.shapes()) {
Expand All @@ -202,25 +196,19 @@ fun AnimatedVisibilityScope.FavouritesScreen(navigator: DestinationsNavigator, v
val height by collectListThumbSizeAsState()
val showPages by Settings.showGalleryPages.collectAsState()
val showProgress by Settings.showReadingProgress.collectAsState()
val searchBarConnection = remember {
val connection = remember {
val slop = ViewConfiguration.get(contextOf<Context>()).scaledTouchSlop
val topPaddingPx = with(density) { contentPadding.calculateTopPadding().roundToPx() }
object : NestedScrollConnection {
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
val dy = -consumed.y
if (dy >= slop) {
fabHidden = true
} else if (dy <= -slop / 2) {
fabHidden = false
}
searchBarOffsetY = (searchBarOffsetY - dy).roundToInt().coerceIn(-topPaddingPx, 0)
return Offset.Zero // We never consume it
scrollBehavior.nestedScrollConnection.watchPostScroll { (_, y) ->
if (-y >= slop) {
fabHidden = true
} else if (-y <= -slop / 2) {
fabHidden = false
}
}
}
GalleryList(
data = data,
contentModifier = Modifier.nestedScroll(searchBarConnection),
contentModifier = Modifier.nestedScroll(connection),
contentPadding = contentPadding,
listMode = listMode,
detailItemContent = { info ->
Expand Down Expand Up @@ -282,10 +270,9 @@ fun AnimatedVisibilityScope.FavouritesScreen(navigator: DestinationsNavigator, v
)
}
},
searchBarOffsetY = { searchBarOffsetY },
scrollToTopOnRefresh = urlBuilder.favCat != FavListUrlBuilder.FAV_CAT_LOCAL,
onRefresh = { refresh() },
onLoading = { searchBarOffsetY = 0 },
onLoading = { scrollBehavior.reset() },
)
}

Expand Down
Loading