Skip to content

Commit

Permalink
feature/scroll-to-top (#75)
Browse files Browse the repository at this point in the history
* Impl scroll to top button
  • Loading branch information
mbakgun authored Aug 13, 2023
1 parent 85d8667 commit 6a29a88
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 10 deletions.
6 changes: 4 additions & 2 deletions shared/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun EmptyScreen( onRefresh: () -&gt; Unit )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun ErrorScreen( onRefresh: () -&gt; Unit )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun MjImageItem( image: MjImage, height: Dp, contentScale: ContentScale, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -&gt; Unit, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun MjImagesList( onLoadMore: () -&gt; Unit, images: MjImages, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -&gt; Unit, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun MjImagesList( images: MjImages, state: LazyStaggeredGridState, onLoadMore: () -&gt; Unit, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -&gt; Unit, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun PreviewDialog( isPreviewVisible: Boolean, imageUrl: String, dialogState: MaterialDialogState = rememberMaterialDialogState() )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun PreviewImage(imageUrl: String)</ID>
<ID>FunctionNaming:MjImagesApp.kt$@OptIn(ExperimentalFoundationApi::class) @Composable fun PlatformSpecificMjImagesGrid( onLoadMore: () -&gt; Unit, images: MjImages, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -&gt; Unit, modifier: Modifier = Modifier, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@Composable fun ScrollToTopButton( onClick: () -&gt; Unit, modifier: Modifier = Modifier )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@OptIn(ExperimentalFoundationApi::class) @Composable fun PlatformSpecificMjImagesGrid( state: LazyStaggeredGridState, images: MjImages, onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -&gt; Unit, onLoadMore: () -&gt; Unit, modifier: Modifier = Modifier, )</ID>
<ID>FunctionNaming:MjImagesApp.kt$@OptIn(ExperimentalMaterialApi::class) @Composable fun MjImagesApp( viewModel: MjImagesViewModel )</ID>
<ID>FunctionNaming:Theme.kt$@Composable fun AppTheme( useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -&gt; Unit )</ID>
<ID>FunctionNaming:main.ios.kt$fun MainViewController(viewModel: MjImagesViewModel): UIViewController</ID>
<ID>LongMethod:MjImagesApp.kt$@OptIn(ExperimentalMaterialApi::class) @Composable fun MjImagesApp( viewModel: MjImagesViewModel )</ID>
<ID>MagicNumber:Colors.kt$0xFF1F1B16</ID>
<ID>MagicNumber:Colors.kt$0xFF3E2D16</ID>
<ID>MagicNumber:Colors.kt$0xFF452B00</ID>
Expand Down
76 changes: 68 additions & 8 deletions shared/src/commonMain/kotlin/ui/MjImagesApp.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
@file:OptIn(ExperimentalFoundationApi::class)

package ui

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
Expand All @@ -16,12 +21,15 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
Expand All @@ -30,6 +38,7 @@ import androidx.compose.material.ScaffoldState
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.rounded.DarkMode
import androidx.compose.material.icons.rounded.LightMode
import androidx.compose.material.pullrefresh.PullRefreshIndicator
Expand All @@ -40,14 +49,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.draw.rotate
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.BlurEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
Expand All @@ -71,6 +83,7 @@ import domain.model.MjImages
import domain.model.State
import kotlin.math.roundToInt
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import ui.theme.AppTheme
import util.OnBottomReached
import util.generateImageLoader
Expand All @@ -91,6 +104,14 @@ fun MjImagesApp(
val onRefresh = viewModel::refreshImages

val scaffoldState: ScaffoldState = rememberScaffoldState()
val listState = rememberLazyStaggeredGridState()
val scope = rememberCoroutineScope()

val showButton by remember {
derivedStateOf {
listState.firstVisibleItemIndex > 0
}
}

LaunchedEffect(Unit) {
if (viewModel.isEligibleToShowSnackBar()) {
Expand All @@ -112,8 +133,9 @@ fun MjImagesApp(
State.ERROR -> ErrorScreen(onRefresh)
State.EMPTY -> EmptyScreen(onRefresh)
else -> MjImagesList(
viewModel::loadMore,
images,
onLoadMore = viewModel::loadMore,
images = images,
state = listState,
) { isPreviewVisible, imageUrl ->
PreviewDialog(isPreviewVisible, imageUrl)
}
Expand All @@ -125,6 +147,18 @@ fun MjImagesApp(
.testTag("pullRefreshIndicator")
)

AnimatedVisibility(
visible = showButton,
enter = fadeIn(),
exit = fadeOut(),
) {
ScrollToTopButton(onClick = {
scope.launch {
listState.animateScrollToItem(0)
}
})
}

DraggableThemeSelection(
useDarkTheme,
viewModel::setDarkMode
Expand All @@ -137,30 +171,31 @@ fun MjImagesApp(

@Composable
fun MjImagesList(
onLoadMore: () -> Unit,
images: MjImages,
state: LazyStaggeredGridState,
onLoadMore: () -> Unit,
onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit,
) {
PlatformSpecificMjImagesGrid(
onLoadMore = onLoadMore,
images = images,
modifier = Modifier.fillMaxSize().testTag("imagesGrid"),
onPreviewVisibilityChanged = onPreviewVisibilityChanged
onPreviewVisibilityChanged = onPreviewVisibilityChanged,
state = state,
)
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PlatformSpecificMjImagesGrid(
onLoadMore: () -> Unit,
state: LazyStaggeredGridState,
images: MjImages,
onPreviewVisibilityChanged: @Composable (isVisible: Boolean, imageUrl: String) -> Unit,
onLoadMore: () -> Unit,
modifier: Modifier = Modifier,
) {
LazyVerticalStaggeredGrid(
state = rememberLazyStaggeredGridState().apply {
OnBottomReached(onLoadMore::invoke)
},
state = state.apply { OnBottomReached(onLoadMore::invoke) },
columns = StaggeredGridCells.Fixed(2),
modifier = modifier,
) {
Expand Down Expand Up @@ -379,5 +414,30 @@ fun DraggableThemeSelection(
}
}

@Composable
fun ScrollToTopButton(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier
.fillMaxSize()
.padding(bottom = 24.dp, end = 25.dp), Alignment.BottomEnd
) {
Button(
onClick = { onClick() }, modifier = Modifier
.shadow(10.dp, shape = CircleShape)
.clip(shape = CircleShape)
.size(65.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.background,
contentColor = MaterialTheme.colors.onSurface,
)
) {
Icon(Icons.Filled.KeyboardArrowUp, "arrow up")
}
}
}

const val SNACK_MESSAGE = "1) Click image to open in browser\n" +
"2) Long click to preview image"

0 comments on commit 6a29a88

Please sign in to comment.