From d5d9b6bea5d28b33efbc318aa7586efad805a42f Mon Sep 17 00:00:00 2001 From: stslex Date: Tue, 5 Oct 2021 16:14:43 +0300 Subject: [PATCH] UI improvements --- app/build.gradle | 5 + .../data/photo/PhotoDataMapper.kt | 10 +- .../csplashscreen/di/module/MapperModule.kt | 2 +- .../st/slex/csplashscreen/ui/MainActivity.kt | 2 +- .../st/slex/csplashscreen/ui/MainViewModel.kt | 15 +- .../slex/csplashscreen/ui/core/UIResponse.kt | 16 +- .../ui/detail/ImageDetailScreen.kt | 133 +++++++------- .../slex/csplashscreen/ui/main/Collections.kt | 65 ------- .../ui/main/CollectionsColumn.kt | 166 ++++++++++++++++++ .../slex/csplashscreen/ui/main/MainScreen.kt | 101 ++++++++--- .../ui/main/{Photos.kt => PhotosColumn.kt} | 31 +--- .../ui/raw_image/rawImageScreen.kt | 7 +- 12 files changed, 347 insertions(+), 206 deletions(-) delete mode 100644 app/src/main/java/st/slex/csplashscreen/ui/main/Collections.kt create mode 100644 app/src/main/java/st/slex/csplashscreen/ui/main/CollectionsColumn.kt rename app/src/main/java/st/slex/csplashscreen/ui/main/{Photos.kt => PhotosColumn.kt} (82%) diff --git a/app/build.gradle b/app/build.gradle index e26c6fd9..0d02c8e7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,6 +54,11 @@ dependencies { implementation "com.google.accompanist:accompanist-pager:$accompanistVersion" implementation "com.google.accompanist:accompanist-placeholder:$accompanistVersion" implementation "com.google.accompanist:accompanist-appcompat-theme:$accompanistVersion" + implementation "com.google.accompanist:accompanist-pager-indicators:$accompanistVersion" + implementation "com.google.accompanist:accompanist-insets:$accompanistVersion" + implementation "com.google.accompanist:accompanist-insets-ui:$accompanistVersion" + implementation "com.google.accompanist:accompanist-navigation-animation:$accompanistVersion" + implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-rc01" /*Navigation*/ implementation "androidx.navigation:navigation-compose:2.4.0-alpha10" diff --git a/app/src/main/java/st/slex/csplashscreen/data/photo/PhotoDataMapper.kt b/app/src/main/java/st/slex/csplashscreen/data/photo/PhotoDataMapper.kt index 1766e901..2d3e5d6e 100644 --- a/app/src/main/java/st/slex/csplashscreen/data/photo/PhotoDataMapper.kt +++ b/app/src/main/java/st/slex/csplashscreen/data/photo/PhotoDataMapper.kt @@ -4,16 +4,14 @@ import st.slex.csplashscreen.data.core.Mapper.DataToUI import st.slex.csplashscreen.data.core.toImageModel import st.slex.csplashscreen.data.model.remote.image.RemoteImageModel import st.slex.csplashscreen.data.model.ui.image.ImageModel -import st.slex.csplashscreen.ui.core.UIResult import javax.inject.Inject class PhotoDataMapper @Inject constructor() : - DataToUI> { + DataToUI { - override fun map(data: RemoteImageModel): UIResult = - UIResult.Success(data.toImageModel()) + override fun map(data: RemoteImageModel): ImageModel = + data.toImageModel() - override fun map(exception: Exception): UIResult = - UIResult.Failure(exception) + override fun map(exception: Exception): ImageModel? = null } \ No newline at end of file diff --git a/app/src/main/java/st/slex/csplashscreen/di/module/MapperModule.kt b/app/src/main/java/st/slex/csplashscreen/di/module/MapperModule.kt index 4ae7bc8f..c1963dd4 100644 --- a/app/src/main/java/st/slex/csplashscreen/di/module/MapperModule.kt +++ b/app/src/main/java/st/slex/csplashscreen/di/module/MapperModule.kt @@ -15,7 +15,7 @@ import st.slex.csplashscreen.ui.core.UIResult class MapperModule { @Provides - fun providesPhotoDataMapper(): Mapper.DataToUI> = + fun providesPhotoDataMapper(): Mapper.DataToUI = PhotoDataMapper() @Provides diff --git a/app/src/main/java/st/slex/csplashscreen/ui/MainActivity.kt b/app/src/main/java/st/slex/csplashscreen/ui/MainActivity.kt index acfa7f1f..e15883f2 100644 --- a/app/src/main/java/st/slex/csplashscreen/ui/MainActivity.kt +++ b/app/src/main/java/st/slex/csplashscreen/ui/MainActivity.kt @@ -99,7 +99,7 @@ fun NavigationComponent(navController: NavHostController, viewModel: MainViewMod route = "raw_image/{url}", arguments = listOf(navArgument("url") { type = NavType.StringType }) ) { - RawImageScreen(url = it.arguments?.getString("url").toString()) + RawImageScreen(url = it.arguments?.getString("url").toString(), navController) } } } \ No newline at end of file diff --git a/app/src/main/java/st/slex/csplashscreen/ui/MainViewModel.kt b/app/src/main/java/st/slex/csplashscreen/ui/MainViewModel.kt index 8cf12a9e..8d03e085 100644 --- a/app/src/main/java/st/slex/csplashscreen/ui/MainViewModel.kt +++ b/app/src/main/java/st/slex/csplashscreen/ui/MainViewModel.kt @@ -25,19 +25,20 @@ class MainViewModel @Inject constructor( private val queryPhotosUseCaseProvider: Provider, private val queryCollectionsUseCaseProvider: Provider, private val repository: PhotoRepository, - private val photoMapper: Mapper.DataToUI>, + private val photoMapper: Mapper.DataToUI, private val downloadMapper: Mapper.DataToUI>, private val response: UIResponse ) : ViewModel() { - - private val _currentPhoto = MutableSharedFlow>(replay = 0) - val currentPhoto: SharedFlow> + private val _currentPhoto = MutableSharedFlow(replay = 0) + val currentPhoto: SharedFlow get() = _currentPhoto fun getCurrentPhoto(id: String) = viewModelScope.launch { repository.getCurrentPhoto(id).collect { - _currentPhoto.emit(it.map(mapper = photoMapper)) + response.getAndMap(repository.getCurrentPhoto(id), mapper = photoMapper).collect { + _currentPhoto.emit(it) + } } } @@ -49,7 +50,7 @@ class MainViewModel @Inject constructor( ) private val _queryPhotos = MutableStateFlow(emptyList()) - val queryPhotos: StateFlow> = _queryPhotos.asStateFlow() + private val queryPhotos: StateFlow> = _queryPhotos.asStateFlow() private var photosPagingSource: PagingSource<*, *>? = null @@ -72,7 +73,7 @@ class MainViewModel @Inject constructor( } private val _queryCollections = MutableStateFlow(emptyList()) - val queryCollections: StateFlow> = _queryCollections.asStateFlow() + private val queryCollections: StateFlow> = _queryCollections.asStateFlow() private var newCollectionsPagingSource: PagingSource<*, *>? = null diff --git a/app/src/main/java/st/slex/csplashscreen/ui/core/UIResponse.kt b/app/src/main/java/st/slex/csplashscreen/ui/core/UIResponse.kt index 10668ddc..fece2d71 100644 --- a/app/src/main/java/st/slex/csplashscreen/ui/core/UIResponse.kt +++ b/app/src/main/java/st/slex/csplashscreen/ui/core/UIResponse.kt @@ -1,5 +1,7 @@ package st.slex.csplashscreen.ui.core +import android.content.ContentValues.TAG +import android.util.Log import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking @@ -15,15 +17,15 @@ interface UIResponse { suspend fun getAndMap( flow: Flow>, - mapper: Mapper.DataToUI> - ): Flow> + mapper: Mapper.DataToUI + ): Flow class Base @Inject constructor() : UIResponse { override suspend fun getAndMap( flow: Flow>, - mapper: Mapper.DataToUI> - ): Flow> = + mapper: Mapper.DataToUI + ): Flow = callbackFlow { flow.startCollecting(mapper) { trySendBlocking(it) @@ -32,14 +34,14 @@ interface UIResponse { } private suspend inline fun Flow>.startCollecting( - mapper: Mapper.DataToUI>, - crossinline function: (UIResult) -> Unit, + mapper: Mapper.DataToUI, + crossinline function: (T) -> Unit, ) = try { collect { function(it.map(mapper)) } } catch (exception: Exception) { - function(UIResult.Failure(exception)) + Log.e(TAG, exception.message, exception.cause) } } } \ No newline at end of file diff --git a/app/src/main/java/st/slex/csplashscreen/ui/detail/ImageDetailScreen.kt b/app/src/main/java/st/slex/csplashscreen/ui/detail/ImageDetailScreen.kt index 49f0847c..6ba0f754 100644 --- a/app/src/main/java/st/slex/csplashscreen/ui/detail/ImageDetailScreen.kt +++ b/app/src/main/java/st/slex/csplashscreen/ui/detail/ImageDetailScreen.kt @@ -2,55 +2,50 @@ package st.slex.csplashscreen.ui.detail import androidx.compose.foundation.Image import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Surface +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.annotation.ExperimentalCoilApi import coil.compose.rememberImagePainter -import coil.transform.BlurTransformation import coil.transform.RoundedCornersTransformation -import com.google.accompanist.placeholder.PlaceholderHighlight -import com.google.accompanist.placeholder.fade -import com.google.accompanist.placeholder.placeholder import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharedFlow -import st.slex.csplashscreen.R import st.slex.csplashscreen.data.model.ui.image.ImageModel -import st.slex.csplashscreen.ui.core.UIResult +import st.slex.csplashscreen.ui.main.UserImageHead +import st.slex.csplashscreen.ui.theme.Shapes +import st.slex.csplashscreen.ui.theme.Typography import java.net.URLEncoder import java.nio.charset.StandardCharsets +@ExperimentalMaterialApi @ExperimentalCoroutinesApi @ExperimentalCoilApi @Composable fun ImageDetailScreen( url: String, navController: NavController, - imageFlow: () -> SharedFlow> + imageFlow: () -> SharedFlow ) { - val imageModel = imageFlow().collectAsState( - initial = UIResult.Loading, - context = rememberCoroutineScope().coroutineContext - ).value - - when (imageModel) { - is UIResult.Success -> BindImage(navController, imageModel.data) - is UIResult.Loading -> BindImageLoading(url = url) - is UIResult.Failure -> BindImageFailure(url = url) - } + val imageModel = imageFlow().collectAsState(initial = null).value + BindDetailImage(navController, imageModel) } +@ExperimentalMaterialApi @ExperimentalCoilApi @Composable -private fun BindImage(navController: NavController, imageModel: ImageModel) { +private fun BindDetailImage(navController: NavController, imageModel: ImageModel?) { Column { Image( modifier = Modifier @@ -59,13 +54,13 @@ private fun BindImage(navController: NavController, imageModel: ImageModel) { .clickable { val encodedUrl = URLEncoder.encode( - imageModel.urls.regular, + imageModel?.urls?.regular, StandardCharsets.UTF_8.toString() ) navController.navigate("raw_image/$encodedUrl") }, painter = rememberImagePainter( - data = imageModel.urls.regular, + data = imageModel?.urls?.regular, builder = { transformations(RoundedCornersTransformation()) allowHardware(false) @@ -73,54 +68,60 @@ private fun BindImage(navController: NavController, imageModel: ImageModel) { ), contentDescription = "TestImage" ) + + Spacer(modifier = Modifier.padding(4.dp)) + + UserImageHead( + modifier = Modifier.padding(start = 16.dp), + username = imageModel?.user?.username.toString(), + url = imageModel?.user?.profile_image?.medium.toString(), + navController = navController + ) } -} -@ExperimentalCoilApi -@Composable -private fun BindImageLoading(url: String) { - androidx.compose.material.Surface( - modifier = Modifier - .height(300.dp) - .fillMaxWidth() - .placeholder( - visible = true, - highlight = PlaceholderHighlight.fade(highlightColor = Color.LightGray), - color = Color.Gray - ) - ) {} } @ExperimentalCoilApi +@ExperimentalMaterialApi @Composable -private fun BindImageFailure(url: String) { - - Image( +fun CurrentImageUser(username: String, url: String, navController: NavController) { + Surface( modifier = Modifier .fillMaxWidth() - .height(300.dp), - painter = rememberImagePainter( - data = url, - builder = { - transformations( - RoundedCornersTransformation(), - BlurTransformation(LocalContext.current) - ) - allowHardware(false) - crossfade(500) - } - ), - contentDescription = "TestImage" - ) - Image( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - painter = rememberImagePainter( - data = R.drawable.ic_baseline_sentiment_very_dissatisfied_24 - ), - contentDescription = "TestImage" - ) + .shadow(elevation = 0.dp, Shapes.medium), + onClick = { + //navController.navigate("user_profile") + } + ) { + Row( + modifier = Modifier + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + modifier = Modifier + .size(48.dp) + .clip(CircleShape), + painter = rememberImagePainter( + data = url, + builder = { + allowHardware(false) + crossfade(500) + } + ), + contentDescription = "User Avatar" + ) + Spacer(modifier = Modifier.padding(8.dp)) + Text( + text = username, + style = Typography.h5, + maxLines = 1, + lineHeight = TextUnit.Unspecified, + fontFamily = FontFamily.SansSerif + ) + } + } + } diff --git a/app/src/main/java/st/slex/csplashscreen/ui/main/Collections.kt b/app/src/main/java/st/slex/csplashscreen/ui/main/Collections.kt deleted file mode 100644 index b418dc43..00000000 --- a/app/src/main/java/st/slex/csplashscreen/ui/main/Collections.kt +++ /dev/null @@ -1,65 +0,0 @@ -package st.slex.csplashscreen.ui.main - -import android.annotation.SuppressLint -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import coil.annotation.ExperimentalCoilApi -import coil.compose.rememberImagePainter -import coil.transform.RoundedCornersTransformation -import com.google.accompanist.pager.ExperimentalPagerApi -import st.slex.csplashscreen.data.model.ui.collection.CollectionModel -import st.slex.csplashscreen.ui.theme.Shapes - -@SuppressLint("RestrictedApi") -@ExperimentalMaterialApi -@ExperimentalPagerApi -@ExperimentalCoilApi -@Composable -fun CollectionItem( - item: CollectionModel?, - navController: NavController -) { - Column( - modifier = Modifier - .padding(16.dp) - ) { - - UserImageHead( - url = item?.user?.profile_image?.medium.toString(), - username = item?.user?.username.toString(), - navController = navController - ) - - Surface( - modifier = Modifier.shadow(elevation = 8.dp, Shapes.medium, clip = true), - onClick = { navController.navigate("collection/${item?.id}") } - ) { - Image( - modifier = Modifier - .fillMaxWidth() - .height(300.dp) - .clipToBounds(), - painter = rememberImagePainter( - data = item?.cover_photo?.urls?.regular.toString(), - builder = { - transformations(RoundedCornersTransformation()) - allowHardware(false) - crossfade(500) - } - ), - contentDescription = "Image", - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/st/slex/csplashscreen/ui/main/CollectionsColumn.kt b/app/src/main/java/st/slex/csplashscreen/ui/main/CollectionsColumn.kt new file mode 100644 index 00000000..91ff9360 --- /dev/null +++ b/app/src/main/java/st/slex/csplashscreen/ui/main/CollectionsColumn.kt @@ -0,0 +1,166 @@ +package st.slex.csplashscreen.ui.main + +import android.annotation.SuppressLint +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.navigation.NavController +import coil.annotation.ExperimentalCoilApi +import coil.compose.rememberImagePainter +import coil.transform.RoundedCornersTransformation +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.PagerScope +import com.google.accompanist.pager.calculateCurrentOffsetForPage +import com.google.android.material.animation.AnimationUtils +import st.slex.csplashscreen.data.model.ui.collection.CollectionModel +import st.slex.csplashscreen.ui.theme.Shapes +import st.slex.csplashscreen.ui.theme.TransparentGray +import st.slex.csplashscreen.ui.theme.Typography + +@SuppressLint("RestrictedApi") +@ExperimentalMaterialApi +@ExperimentalPagerApi +@ExperimentalCoilApi +@Composable +fun CollectionItem( + item: CollectionModel?, + navController: NavController, + page: Int = 0, + scope: PagerScope? = null +) { + Column( + modifier = Modifier + .padding(16.dp) + .graphicsLayer { + scope?.let { + val pageOffset = it.calculateCurrentOffsetForPage(page) + AnimationUtils + .lerp( + 0.85f, + 1f, + 1f - pageOffset.coerceIn(0f, 1f) + ) + .also { scale -> + scaleX = scale + scaleY = scale + } + alpha = AnimationUtils.lerp( + 0.5f, + 1f, + 1f - pageOffset.coerceIn(0f, 1f) + ) + } + } + .fillMaxWidth() + .aspectRatio(1f)) { + + UserImageHead( + modifier = Modifier, + url = item?.user?.profile_image?.medium.toString(), + username = item?.user?.username.toString(), + navController = navController + ) + + Spacer(modifier = Modifier.padding(4.dp)) + + Surface( + modifier = Modifier + .shadow(elevation = 16.dp, shape = Shapes.medium, clip = true), + onClick = { navController.navigate("collection/${item?.id}") }, + ) { + BindCoverImageConstraint( + item?.cover_photo?.urls?.regular.toString(), + item?.title.toString(), + item?.total_photos.toString() + ) + } + } + +} + +@ExperimentalCoilApi +@ExperimentalMaterialApi +@ExperimentalPagerApi +@Composable +fun BindCoverImageConstraint( + url: String, + title: String, + totalPhotos: String +) { + ConstraintLayout( + modifier = Modifier + .fillMaxSize() + .background(color = MaterialTheme.colors.surface) + ) { + val (background, content) = createRefs() // 1 + + CoverPhotoItem(url) + + Surface( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .constrainAs(background) { + centerTo(parent) + }, + color = TransparentGray + ) {} + + Column(modifier = Modifier + .fillMaxWidth() + .padding(32.dp) + .constrainAs(content) { + bottom.linkTo(parent.bottom) + }) { + CollectionTextCard(text = title) + Spacer(modifier = Modifier.padding(8.dp)) + CollectionTextCard(text = "${totalPhotos} Photos") + } + } +} + +@Composable +fun CollectionTextCard(text: String) { + Text( + text = text, + textAlign = TextAlign.Start, + color = Color.White, + style = Typography.h3, + maxLines = 1 + ) +} + +@ExperimentalCoilApi +@ExperimentalMaterialApi +@Composable +fun CoverPhotoItem(url: String) { + Image( + modifier = Modifier + .fillMaxWidth() + .height(300.dp) + .clipToBounds() + .background(Color.Black), + painter = rememberImagePainter( + data = url, + builder = { + transformations(RoundedCornersTransformation()) + allowHardware(false) + crossfade(500) + } + ), + contentDescription = "Image", + ) +} \ No newline at end of file diff --git a/app/src/main/java/st/slex/csplashscreen/ui/main/MainScreen.kt b/app/src/main/java/st/slex/csplashscreen/ui/main/MainScreen.kt index 85ff9ec6..b8ab8c2b 100644 --- a/app/src/main/java/st/slex/csplashscreen/ui/main/MainScreen.kt +++ b/app/src/main/java/st/slex/csplashscreen/ui/main/MainScreen.kt @@ -4,21 +4,26 @@ import android.annotation.SuppressLint import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items import coil.annotation.ExperimentalCoilApi -import com.google.accompanist.pager.ExperimentalPagerApi -import com.google.accompanist.pager.HorizontalPager -import com.google.accompanist.pager.PagerScope +import com.google.accompanist.pager.* import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import st.slex.csplashscreen.data.model.ui.collection.CollectionModel import st.slex.csplashscreen.data.model.ui.image.ImageModel import st.slex.csplashscreen.ui.MainViewModel +import st.slex.csplashscreen.ui.theme.Typography import st.slex.csplashscreen.utiles.GET_COLLECTIONS import st.slex.csplashscreen.utiles.GET_PHOTOS @@ -38,7 +43,6 @@ fun MainScreen(navController: NavController, viewModel: MainViewModel) { val lazyPagingPhotosItems = viewModel.photos.collectAsLazyPagingItems() val lazyPagingCollectionsItems = viewModel.collections.collectAsLazyPagingItems() - Pager(lazyPagingPhotosItems, lazyPagingCollectionsItems, navController) } @@ -52,37 +56,70 @@ fun Pager( lazyPagingCollectionsItems: LazyPagingItems, navController: NavController ) { - val mapPages = mutableMapOf( - "Photos" to 0, - "Collections" to 1 - ) - - HorizontalPager( - count = mapPages.size, - contentPadding = PaddingValues(), - ) { page -> - LazyColumn { - when (page) { - mapPages.getValue("Photos") -> { - items(lazyPagingPhotosItems) { item -> - ImageItem(item, navController, page, this@HorizontalPager) - } - lazyPagingPhotosItems.checkState { - loadState(page = page, scope = this@HorizontalPager) + val scope = rememberCoroutineScope() + val pagerState = rememberPagerState() + + LaunchedEffect(pagerState) { + snapshotFlow { pagerState.currentPage }.collect { page -> + AnalyticsService.sendPageSelectedEvent(page) + } + } + + val pages = listOf("Photos", "Collections") + + Column { + TabRow( + selectedTabIndex = pagerState.currentPage, + indicator = { tabPositions -> + TabRowDefaults.Indicator( + Modifier.pagerTabIndicatorOffset(pagerState, tabPositions) + ) + } + ) { + pages.forEachIndexed { index, title -> + Tab( + text = { + Text( + text = title, + style = Typography.subtitle1 + ) + }, + selected = pagerState.currentPage == index, + onClick = { + scope.launch { pagerState.scrollToPage(index) } } - } - mapPages.getValue("Collections") -> { - items(lazyPagingCollectionsItems) { item -> - CollectionItem(item, navController) + ) + } + } + + HorizontalPager( + count = pages.size, + state = pagerState + ) { page -> + LazyColumn { + when (page) { + pages.indexOf("Photos") -> { + items(lazyPagingPhotosItems) { item -> + ImageItem(item, navController, page, this@HorizontalPager) + } + lazyPagingPhotosItems.checkState { + loadState(page = page, scope = this@HorizontalPager) + } } - lazyPagingCollectionsItems.checkState { - loadState(page = page, scope = this@HorizontalPager) + pages.indexOf("Collections") -> { + items(lazyPagingCollectionsItems) { item -> + CollectionItem(item, navController, page, this@HorizontalPager) + } + lazyPagingCollectionsItems.checkState { + loadState(page = page, scope = this@HorizontalPager) + } } } - } + } } } + } @ExperimentalCoilApi @@ -100,4 +137,10 @@ inline fun LazyPagingItems.checkState(crossinline function: () -> U } } +@Suppress("UNUSED_PARAMETER") +object AnalyticsService { + fun sendPageSelectedEvent(page: Int) = Unit +} + + diff --git a/app/src/main/java/st/slex/csplashscreen/ui/main/Photos.kt b/app/src/main/java/st/slex/csplashscreen/ui/main/PhotosColumn.kt similarity index 82% rename from app/src/main/java/st/slex/csplashscreen/ui/main/Photos.kt rename to app/src/main/java/st/slex/csplashscreen/ui/main/PhotosColumn.kt index 57e57b37..5cd1a8f6 100644 --- a/app/src/main/java/st/slex/csplashscreen/ui/main/Photos.kt +++ b/app/src/main/java/st/slex/csplashscreen/ui/main/PhotosColumn.kt @@ -11,7 +11,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.text.font.FontFamily @@ -20,7 +19,6 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.annotation.ExperimentalCoilApi import coil.compose.rememberImagePainter -import coil.transform.RoundedCornersTransformation import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.PagerScope import com.google.accompanist.pager.calculateCurrentOffsetForPage @@ -66,14 +64,18 @@ fun ImageItem( ) } } + .fillMaxWidth() .aspectRatio(1f)) { UserImageHead( + modifier = Modifier, url = item?.user?.profile_image?.medium.toString(), username = item?.user?.username.toString(), navController = navController ) + Spacer(modifier = Modifier.padding(4.dp)) + Surface( modifier = Modifier.shadow(elevation = 16.dp, shape = Shapes.medium, clip = true), onClick = { @@ -83,21 +85,7 @@ fun ImageItem( navController.navigate("detail/$encodedUrl/$id") } ) { - Image( - modifier = Modifier - .fillMaxWidth() - .height(300.dp) - .clipToBounds(), - painter = rememberImagePainter( - data = item?.urls?.regular, - builder = { - transformations(RoundedCornersTransformation()) - allowHardware(false) - crossfade(500) - } - ), - contentDescription = "Image", - ) + CoverPhotoItem(item?.urls?.regular.toString()) } } } @@ -106,10 +94,10 @@ fun ImageItem( @ExperimentalMaterialApi @ExperimentalCoilApi @Composable -fun UserImageHead(url: String, username: String, navController: NavController) { +fun UserImageHead(modifier: Modifier, url: String, username: String, navController: NavController) { Surface( - modifier = Modifier + modifier = modifier .fillMaxWidth() .shadow(elevation = 0.dp, Shapes.medium), onClick = { @@ -123,7 +111,7 @@ fun UserImageHead(url: String, username: String, navController: NavController) { ) { Image( modifier = Modifier - .size(48.dp) + .size(32.dp) .clip(CircleShape), painter = rememberImagePainter( data = url, @@ -137,12 +125,11 @@ fun UserImageHead(url: String, username: String, navController: NavController) { Spacer(modifier = Modifier.padding(8.dp)) Text( text = username, - style = Typography.h5, + style = Typography.h6, maxLines = 1, lineHeight = TextUnit.Unspecified, fontFamily = FontFamily.SansSerif ) } } - } diff --git a/app/src/main/java/st/slex/csplashscreen/ui/raw_image/rawImageScreen.kt b/app/src/main/java/st/slex/csplashscreen/ui/raw_image/rawImageScreen.kt index 1610a17f..ccaad15a 100644 --- a/app/src/main/java/st/slex/csplashscreen/ui/raw_image/rawImageScreen.kt +++ b/app/src/main/java/st/slex/csplashscreen/ui/raw_image/rawImageScreen.kt @@ -2,22 +2,25 @@ package st.slex.csplashscreen.ui.raw_image import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.navigation.NavController import coil.annotation.ExperimentalCoilApi import coil.compose.rememberImagePainter @ExperimentalCoilApi @Composable -fun RawImageScreen(url: String) { +fun RawImageScreen(url: String, navController: NavController) { Row( modifier = Modifier .fillMaxSize() - .background(Color.Black), + .background(Color.Black) + .clickable { navController.popBackStack() }, verticalAlignment = Alignment.CenterVertically ) { Image(