Skip to content

Commit

Permalink
UI improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
stslex committed Oct 5, 2021
1 parent 900cc8e commit d5d9b6b
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 206 deletions.
5 changes: 5 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<RemoteImageModel, UIResult<ImageModel>> {
DataToUI<RemoteImageModel, ImageModel?> {

override fun map(data: RemoteImageModel): UIResult<ImageModel> =
UIResult.Success(data.toImageModel())
override fun map(data: RemoteImageModel): ImageModel =
data.toImageModel()

override fun map(exception: Exception): UIResult<ImageModel> =
UIResult.Failure(exception)
override fun map(exception: Exception): ImageModel? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import st.slex.csplashscreen.ui.core.UIResult
class MapperModule {

@Provides
fun providesPhotoDataMapper(): Mapper.DataToUI<RemoteImageModel, UIResult<ImageModel>> =
fun providesPhotoDataMapper(): Mapper.DataToUI<RemoteImageModel, ImageModel?> =
PhotoDataMapper()

@Provides
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/st/slex/csplashscreen/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
15 changes: 8 additions & 7 deletions app/src/main/java/st/slex/csplashscreen/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ class MainViewModel @Inject constructor(
private val queryPhotosUseCaseProvider: Provider<QueryPhotosUseCase>,
private val queryCollectionsUseCaseProvider: Provider<QueryCollectionsUseCase>,
private val repository: PhotoRepository,
private val photoMapper: Mapper.DataToUI<RemoteImageModel, UIResult<ImageModel>>,
private val photoMapper: Mapper.DataToUI<RemoteImageModel, ImageModel>,
private val downloadMapper: Mapper.DataToUI<RemoteDownloadModel, UIResult<DownloadModel>>,
private val response: UIResponse
) : ViewModel() {


private val _currentPhoto = MutableSharedFlow<UIResult<ImageModel>>(replay = 0)
val currentPhoto: SharedFlow<UIResult<ImageModel>>
private val _currentPhoto = MutableSharedFlow<ImageModel>(replay = 0)
val currentPhoto: SharedFlow<ImageModel>
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)
}
}
}

Expand All @@ -49,7 +50,7 @@ class MainViewModel @Inject constructor(
)

private val _queryPhotos = MutableStateFlow(emptyList<String>())
val queryPhotos: StateFlow<List<String>> = _queryPhotos.asStateFlow()
private val queryPhotos: StateFlow<List<String>> = _queryPhotos.asStateFlow()

private var photosPagingSource: PagingSource<*, *>? = null

Expand All @@ -72,7 +73,7 @@ class MainViewModel @Inject constructor(
}

private val _queryCollections = MutableStateFlow(emptyList<String>())
val queryCollections: StateFlow<List<String>> = _queryCollections.asStateFlow()
private val queryCollections: StateFlow<List<String>> = _queryCollections.asStateFlow()

private var newCollectionsPagingSource: PagingSource<*, *>? = null

Expand Down
16 changes: 9 additions & 7 deletions app/src/main/java/st/slex/csplashscreen/ui/core/UIResponse.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,15 +17,15 @@ interface UIResponse {

suspend fun <D, U> getAndMap(
flow: Flow<DataResult<D>>,
mapper: Mapper.DataToUI<D, UIResult<U>>
): Flow<UIResult<U>>
mapper: Mapper.DataToUI<D, U>
): Flow<U>

class Base @Inject constructor() : UIResponse {

override suspend fun <D, U> getAndMap(
flow: Flow<DataResult<D>>,
mapper: Mapper.DataToUI<D, UIResult<U>>
): Flow<UIResult<U>> =
mapper: Mapper.DataToUI<D, U>
): Flow<U> =
callbackFlow {
flow.startCollecting(mapper) {
trySendBlocking(it)
Expand All @@ -32,14 +34,14 @@ interface UIResponse {
}

private suspend inline fun <D, T> Flow<DataResult<D>>.startCollecting(
mapper: Mapper.DataToUI<D, UIResult<T>>,
crossinline function: (UIResult<T>) -> Unit,
mapper: Mapper.DataToUI<D, T>,
crossinline function: (T) -> Unit,
) = try {
collect {
function(it.map(mapper))
}
} catch (exception: Exception) {
function(UIResult.Failure(exception))
Log.e(TAG, exception.message, exception.cause)
}
}
}
133 changes: 67 additions & 66 deletions app/src/main/java/st/slex/csplashscreen/ui/detail/ImageDetailScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<UIResult<ImageModel>>
imageFlow: () -> SharedFlow<ImageModel>
) {
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
Expand All @@ -59,68 +54,74 @@ 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)
}
),
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
)
}
}

}

Loading

0 comments on commit d5d9b6b

Please sign in to comment.