From 596a1f9ec7309e7888ea5f3d34a0a414c558c16f Mon Sep 17 00:00:00 2001 From: longnghia <41385034+longnghia@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:35:51 +0700 Subject: [PATCH 1/2] Remove Scaffold in images page --- .../hviewer/ui/page/post/PostPage.kt | 56 +++++-------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt b/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt index 8a9e9d6..b2c51b1 100644 --- a/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt +++ b/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt @@ -1,21 +1,17 @@ package com.paulcoding.hviewer.ui.page.post import android.widget.Toast -import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface -import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -34,11 +30,8 @@ import coil3.compose.AsyncImage import com.paulcoding.hviewer.MainApp.Companion.appContext import com.paulcoding.hviewer.extensions.isScrolledToEnd import com.paulcoding.hviewer.helper.makeToast -import com.paulcoding.hviewer.model.SiteConfig -import com.paulcoding.hviewer.ui.component.HBackIcon import com.paulcoding.hviewer.ui.component.HImage import com.paulcoding.hviewer.ui.component.HLoading -import com.paulcoding.hviewer.ui.component.HPageProgress import com.paulcoding.hviewer.ui.component.HideSystemBars import com.paulcoding.hviewer.ui.page.AppViewModel import me.saket.telephoto.zoomable.DoubleClickToZoomListener @@ -96,44 +89,25 @@ fun PostPage(appViewModel: AppViewModel, goBack: () -> Unit) { HideSystemBars() - Scaffold(topBar = { - AnimatedVisibility( - visible = isScrollingUp - ) { - TopAppBar( - navigationIcon = { - HBackIcon { goBack() } - }, - title = {}, - actions = { - if (uiState.images.isNotEmpty()) - HPageProgress(uiState.postPage, uiState.postTotalPage) - } + LazyColumn( + state = listState, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(uiState.images) { image -> + HImage( + modifier = Modifier.clickable { selectedImage = image }, + url = image ) } - }) { - - LazyColumn( - state = listState, - modifier = Modifier.padding(it), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - items(uiState.images) { image -> - HImage( - modifier = Modifier.clickable { selectedImage = image }, - url = image - ) + if (uiState.isLoading) + item { + HLoading() } - if (uiState.isLoading) - item { - HLoading() - } - } + } - if (selectedImage != null) { - ImageModal(url = selectedImage!!) { - selectedImage = null - } + if (selectedImage != null) { + ImageModal(url = selectedImage!!) { + selectedImage = null } } } From 354bbda1949b55bcfbb007e233a450990ade6afe Mon Sep 17 00:00:00 2001 From: longnghia <41385034+longnghia@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:25:29 +0700 Subject: [PATCH 2/2] Add scroll to top button --- .../hviewer/extensions/ListExtensions.kt | 23 +++++++ .../paulcoding/hviewer/ui/component/HGoTop.kt | 43 +++++++++++++ .../hviewer/ui/page/post/PostPage.kt | 60 +++++++------------ .../hviewer/ui/page/posts/PostsPage.kt | 43 +++++++------ .../hviewer/ui/page/search/SearchPage.kt | 39 +++++++----- 5 files changed, 135 insertions(+), 73 deletions(-) create mode 100644 app/src/main/java/com/paulcoding/hviewer/ui/component/HGoTop.kt diff --git a/app/src/main/java/com/paulcoding/hviewer/extensions/ListExtensions.kt b/app/src/main/java/com/paulcoding/hviewer/extensions/ListExtensions.kt index 57b217b..3919f93 100644 --- a/app/src/main/java/com/paulcoding/hviewer/extensions/ListExtensions.kt +++ b/app/src/main/java/com/paulcoding/hviewer/extensions/ListExtensions.kt @@ -1,7 +1,30 @@ package com.paulcoding.hviewer.extensions import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.produceState +import androidx.compose.runtime.snapshotFlow fun LazyListState.isScrolledToEnd(): Boolean { return layoutInfo.visibleItemsInfo.lastOrNull()?.index == layoutInfo.totalItemsCount - 1 } + +@Composable +fun LazyListState.isScrollingUp(): State { + return produceState(initialValue = true) { + var previousIndex = firstVisibleItemIndex + var previousOffset = firstVisibleItemScrollOffset + + snapshotFlow { firstVisibleItemIndex to firstVisibleItemScrollOffset } + .collect { (index, offset) -> + value = if (index != previousIndex) { + index < previousIndex + } else { + offset < previousOffset + } + previousIndex = index + previousOffset = offset + } + } +} diff --git a/app/src/main/java/com/paulcoding/hviewer/ui/component/HGoTop.kt b/app/src/main/java/com/paulcoding/hviewer/ui/component/HGoTop.kt new file mode 100644 index 0000000..1d161db --- /dev/null +++ b/app/src/main/java/com/paulcoding/hviewer/ui/component/HGoTop.kt @@ -0,0 +1,43 @@ +package com.paulcoding.hviewer.ui.component + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.KeyboardArrowUp +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.paulcoding.hviewer.extensions.isScrollingUp +import com.paulcoding.hviewer.ui.page.fadeInWithBlur +import com.paulcoding.hviewer.ui.page.fadeOutWithBlur +import kotlinx.coroutines.launch + +@Composable +fun BoxScope.HGoTop(listState: LazyListState) { + val scope = rememberCoroutineScope() + + AnimatedVisibility( + listState.isScrollingUp().value, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(16.dp), + enter = fadeInWithBlur(), + exit = fadeOutWithBlur(), + ) { + FloatingActionButton( + onClick = { + scope.launch { + listState.animateScrollToItem(0, 0) + } + }, + ) { + Icon(Icons.Outlined.KeyboardArrowUp, "Go to Top") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt b/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt index b2c51b1..f8a9124 100644 --- a/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt +++ b/app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -19,7 +18,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -30,6 +28,7 @@ import coil3.compose.AsyncImage import com.paulcoding.hviewer.MainApp.Companion.appContext import com.paulcoding.hviewer.extensions.isScrolledToEnd import com.paulcoding.hviewer.helper.makeToast +import com.paulcoding.hviewer.ui.component.HGoTop import com.paulcoding.hviewer.ui.component.HImage import com.paulcoding.hviewer.ui.component.HLoading import com.paulcoding.hviewer.ui.component.HideSystemBars @@ -39,7 +38,6 @@ import me.saket.telephoto.zoomable.ZoomSpec import me.saket.telephoto.zoomable.rememberZoomableState import me.saket.telephoto.zoomable.zoomable -@OptIn(ExperimentalMaterial3Api::class) @Composable fun PostPage(appViewModel: AppViewModel, goBack: () -> Unit) { val appState by appViewModel.stateFlow.collectAsState() @@ -68,46 +66,32 @@ fun PostPage(appViewModel: AppViewModel, goBack: () -> Unit) { } } - var isScrollingUp by remember { mutableStateOf(false) } - - // Detect scroll direction - LaunchedEffect(listState) { - var previousIndex = listState.firstVisibleItemIndex - var previousOffset = listState.firstVisibleItemScrollOffset + HideSystemBars() - snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset } - .collect { (index, offset) -> - isScrollingUp = if (index != previousIndex) { - index < previousIndex - } else { - offset < previousOffset - } - previousIndex = index - previousOffset = offset + Box(modifier = Modifier.fillMaxSize()) { + LazyColumn( + state = listState, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(uiState.images) { image -> + HImage( + modifier = Modifier.clickable { selectedImage = image }, + url = image + ) } - } - - HideSystemBars() + if (uiState.isLoading) + item { + HLoading() + } - LazyColumn( - state = listState, - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - items(uiState.images) { image -> - HImage( - modifier = Modifier.clickable { selectedImage = image }, - url = image - ) } - if (uiState.isLoading) - item { - HLoading() - } - } - if (selectedImage != null) { - ImageModal(url = selectedImage!!) { - selectedImage = null + HGoTop(listState) + + if (selectedImage != null) { + ImageModal(url = selectedImage!!) { + selectedImage = null + } } } } diff --git a/app/src/main/java/com/paulcoding/hviewer/ui/page/posts/PostsPage.kt b/app/src/main/java/com/paulcoding/hviewer/ui/page/posts/PostsPage.kt index 773763f..2f9317f 100644 --- a/app/src/main/java/com/paulcoding/hviewer/ui/page/posts/PostsPage.kt +++ b/app/src/main/java/com/paulcoding/hviewer/ui/page/posts/PostsPage.kt @@ -2,6 +2,7 @@ package com.paulcoding.hviewer.ui.page.posts import android.widget.Toast import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -36,6 +37,7 @@ import com.paulcoding.hviewer.model.PostItem import com.paulcoding.hviewer.model.SiteConfig import com.paulcoding.hviewer.ui.component.HBackIcon import com.paulcoding.hviewer.ui.component.HEmpty +import com.paulcoding.hviewer.ui.component.HGoTop import com.paulcoding.hviewer.ui.component.HIcon import com.paulcoding.hviewer.ui.component.HImage import com.paulcoding.hviewer.ui.component.HLoading @@ -144,27 +146,30 @@ fun PageContent( onPageChange(uiState.postsPage, uiState.postsTotalPage) } - LazyColumn( - state = listState - ) { - items(uiState.postItems) { post -> - PostItemView(post) { - onClick(post) - } - } - if (uiState.isLoading) - item { - HLoading() - } - else if (uiState.postItems.isEmpty()) - item { - HEmpty( - title = "No posts found", - message = "Refresh?" - ) { - viewModel.getPosts(1) + Box(modifier = Modifier.fillMaxSize()) { + LazyColumn( + state = listState + ) { + items(uiState.postItems) { post -> + PostItemView(post) { + onClick(post) } } + if (uiState.isLoading) + item { + HLoading() + } + else if (uiState.postItems.isEmpty()) + item { + HEmpty( + title = "No posts found", + message = "Refresh?" + ) { + viewModel.getPosts(1) + } + } + } + HGoTop(listState) } } diff --git a/app/src/main/java/com/paulcoding/hviewer/ui/page/search/SearchPage.kt b/app/src/main/java/com/paulcoding/hviewer/ui/page/search/SearchPage.kt index 99a8b1a..d84c75d 100644 --- a/app/src/main/java/com/paulcoding/hviewer/ui/page/search/SearchPage.kt +++ b/app/src/main/java/com/paulcoding/hviewer/ui/page/search/SearchPage.kt @@ -2,8 +2,10 @@ package com.paulcoding.hviewer.ui.page.search import android.widget.Toast import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -37,6 +39,7 @@ import com.paulcoding.hviewer.extensions.isScrolledToEnd import com.paulcoding.hviewer.model.PostItem import com.paulcoding.hviewer.ui.component.HBackIcon import com.paulcoding.hviewer.ui.component.HEmpty +import com.paulcoding.hviewer.ui.component.HGoTop import com.paulcoding.hviewer.ui.component.HLoading import com.paulcoding.hviewer.ui.component.HPageProgress import com.paulcoding.hviewer.ui.icon.EditIcon @@ -130,23 +133,27 @@ fun PageContent( } } - LazyColumn( - state = listState - ) { - items(uiState.postItems) { post -> - PostItemView(post) { - onClick(post) + Box(modifier = Modifier.fillMaxSize()) { + LazyColumn( + state = listState + ) { + items(uiState.postItems) { post -> + PostItemView(post) { + onClick(post) + } } + if (uiState.isLoading) + item { + HLoading() + } + else if (uiState.postItems.isEmpty() && uiState.query.isNotEmpty()) + item { + HEmpty( + title = "No posts found", + ) + } } - if (uiState.isLoading) - item { - HLoading() - } - else if (uiState.postItems.isEmpty() && uiState.query.isNotEmpty()) - item { - HEmpty( - title = "No posts found", - ) - } + HGoTop(listState) } } +