Skip to content

Commit d5d9b6b

Browse files
committed
UI improvements
1 parent 900cc8e commit d5d9b6b

File tree

12 files changed

+347
-206
lines changed

12 files changed

+347
-206
lines changed

app/build.gradle

+5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ dependencies {
5454
implementation "com.google.accompanist:accompanist-pager:$accompanistVersion"
5555
implementation "com.google.accompanist:accompanist-placeholder:$accompanistVersion"
5656
implementation "com.google.accompanist:accompanist-appcompat-theme:$accompanistVersion"
57+
implementation "com.google.accompanist:accompanist-pager-indicators:$accompanistVersion"
58+
implementation "com.google.accompanist:accompanist-insets:$accompanistVersion"
59+
implementation "com.google.accompanist:accompanist-insets-ui:$accompanistVersion"
60+
implementation "com.google.accompanist:accompanist-navigation-animation:$accompanistVersion"
61+
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-rc01"
5762

5863
/*Navigation*/
5964
implementation "androidx.navigation:navigation-compose:2.4.0-alpha10"

app/src/main/java/st/slex/csplashscreen/data/photo/PhotoDataMapper.kt

+4-6
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@ import st.slex.csplashscreen.data.core.Mapper.DataToUI
44
import st.slex.csplashscreen.data.core.toImageModel
55
import st.slex.csplashscreen.data.model.remote.image.RemoteImageModel
66
import st.slex.csplashscreen.data.model.ui.image.ImageModel
7-
import st.slex.csplashscreen.ui.core.UIResult
87
import javax.inject.Inject
98

109

1110
class PhotoDataMapper @Inject constructor() :
12-
DataToUI<RemoteImageModel, UIResult<ImageModel>> {
11+
DataToUI<RemoteImageModel, ImageModel?> {
1312

14-
override fun map(data: RemoteImageModel): UIResult<ImageModel> =
15-
UIResult.Success(data.toImageModel())
13+
override fun map(data: RemoteImageModel): ImageModel =
14+
data.toImageModel()
1615

17-
override fun map(exception: Exception): UIResult<ImageModel> =
18-
UIResult.Failure(exception)
16+
override fun map(exception: Exception): ImageModel? = null
1917
}

app/src/main/java/st/slex/csplashscreen/di/module/MapperModule.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import st.slex.csplashscreen.ui.core.UIResult
1515
class MapperModule {
1616

1717
@Provides
18-
fun providesPhotoDataMapper(): Mapper.DataToUI<RemoteImageModel, UIResult<ImageModel>> =
18+
fun providesPhotoDataMapper(): Mapper.DataToUI<RemoteImageModel, ImageModel?> =
1919
PhotoDataMapper()
2020

2121
@Provides

app/src/main/java/st/slex/csplashscreen/ui/MainActivity.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ fun NavigationComponent(navController: NavHostController, viewModel: MainViewMod
9999
route = "raw_image/{url}",
100100
arguments = listOf(navArgument("url") { type = NavType.StringType })
101101
) {
102-
RawImageScreen(url = it.arguments?.getString("url").toString())
102+
RawImageScreen(url = it.arguments?.getString("url").toString(), navController)
103103
}
104104
}
105105
}

app/src/main/java/st/slex/csplashscreen/ui/MainViewModel.kt

+8-7
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,20 @@ class MainViewModel @Inject constructor(
2525
private val queryPhotosUseCaseProvider: Provider<QueryPhotosUseCase>,
2626
private val queryCollectionsUseCaseProvider: Provider<QueryCollectionsUseCase>,
2727
private val repository: PhotoRepository,
28-
private val photoMapper: Mapper.DataToUI<RemoteImageModel, UIResult<ImageModel>>,
28+
private val photoMapper: Mapper.DataToUI<RemoteImageModel, ImageModel>,
2929
private val downloadMapper: Mapper.DataToUI<RemoteDownloadModel, UIResult<DownloadModel>>,
3030
private val response: UIResponse
3131
) : ViewModel() {
3232

33-
34-
private val _currentPhoto = MutableSharedFlow<UIResult<ImageModel>>(replay = 0)
35-
val currentPhoto: SharedFlow<UIResult<ImageModel>>
33+
private val _currentPhoto = MutableSharedFlow<ImageModel>(replay = 0)
34+
val currentPhoto: SharedFlow<ImageModel>
3635
get() = _currentPhoto
3736

3837
fun getCurrentPhoto(id: String) = viewModelScope.launch {
3938
repository.getCurrentPhoto(id).collect {
40-
_currentPhoto.emit(it.map(mapper = photoMapper))
39+
response.getAndMap(repository.getCurrentPhoto(id), mapper = photoMapper).collect {
40+
_currentPhoto.emit(it)
41+
}
4142
}
4243
}
4344

@@ -49,7 +50,7 @@ class MainViewModel @Inject constructor(
4950
)
5051

5152
private val _queryPhotos = MutableStateFlow(emptyList<String>())
52-
val queryPhotos: StateFlow<List<String>> = _queryPhotos.asStateFlow()
53+
private val queryPhotos: StateFlow<List<String>> = _queryPhotos.asStateFlow()
5354

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

@@ -72,7 +73,7 @@ class MainViewModel @Inject constructor(
7273
}
7374

7475
private val _queryCollections = MutableStateFlow(emptyList<String>())
75-
val queryCollections: StateFlow<List<String>> = _queryCollections.asStateFlow()
76+
private val queryCollections: StateFlow<List<String>> = _queryCollections.asStateFlow()
7677

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

app/src/main/java/st/slex/csplashscreen/ui/core/UIResponse.kt

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package st.slex.csplashscreen.ui.core
22

3+
import android.content.ContentValues.TAG
4+
import android.util.Log
35
import kotlinx.coroutines.ExperimentalCoroutinesApi
46
import kotlinx.coroutines.channels.awaitClose
57
import kotlinx.coroutines.channels.trySendBlocking
@@ -15,15 +17,15 @@ interface UIResponse {
1517

1618
suspend fun <D, U> getAndMap(
1719
flow: Flow<DataResult<D>>,
18-
mapper: Mapper.DataToUI<D, UIResult<U>>
19-
): Flow<UIResult<U>>
20+
mapper: Mapper.DataToUI<D, U>
21+
): Flow<U>
2022

2123
class Base @Inject constructor() : UIResponse {
2224

2325
override suspend fun <D, U> getAndMap(
2426
flow: Flow<DataResult<D>>,
25-
mapper: Mapper.DataToUI<D, UIResult<U>>
26-
): Flow<UIResult<U>> =
27+
mapper: Mapper.DataToUI<D, U>
28+
): Flow<U> =
2729
callbackFlow {
2830
flow.startCollecting(mapper) {
2931
trySendBlocking(it)
@@ -32,14 +34,14 @@ interface UIResponse {
3234
}
3335

3436
private suspend inline fun <D, T> Flow<DataResult<D>>.startCollecting(
35-
mapper: Mapper.DataToUI<D, UIResult<T>>,
36-
crossinline function: (UIResult<T>) -> Unit,
37+
mapper: Mapper.DataToUI<D, T>,
38+
crossinline function: (T) -> Unit,
3739
) = try {
3840
collect {
3941
function(it.map(mapper))
4042
}
4143
} catch (exception: Exception) {
42-
function(UIResult.Failure(exception))
44+
Log.e(TAG, exception.message, exception.cause)
4345
}
4446
}
4547
}

app/src/main/java/st/slex/csplashscreen/ui/detail/ImageDetailScreen.kt

+67-66
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,50 @@ package st.slex.csplashscreen.ui.detail
22

33
import androidx.compose.foundation.Image
44
import androidx.compose.foundation.clickable
5-
import androidx.compose.foundation.layout.Column
6-
import androidx.compose.foundation.layout.fillMaxWidth
7-
import androidx.compose.foundation.layout.height
5+
import androidx.compose.foundation.layout.*
6+
import androidx.compose.foundation.shape.CircleShape
7+
import androidx.compose.material.ExperimentalMaterialApi
8+
import androidx.compose.material.Surface
9+
import androidx.compose.material.Text
810
import androidx.compose.runtime.Composable
911
import androidx.compose.runtime.collectAsState
10-
import androidx.compose.runtime.rememberCoroutineScope
12+
import androidx.compose.ui.Alignment
1113
import androidx.compose.ui.Modifier
12-
import androidx.compose.ui.graphics.Color
13-
import androidx.compose.ui.platform.LocalContext
14+
import androidx.compose.ui.draw.clip
15+
import androidx.compose.ui.draw.shadow
16+
import androidx.compose.ui.text.font.FontFamily
17+
import androidx.compose.ui.unit.TextUnit
1418
import androidx.compose.ui.unit.dp
1519
import androidx.navigation.NavController
1620
import coil.annotation.ExperimentalCoilApi
1721
import coil.compose.rememberImagePainter
18-
import coil.transform.BlurTransformation
1922
import coil.transform.RoundedCornersTransformation
20-
import com.google.accompanist.placeholder.PlaceholderHighlight
21-
import com.google.accompanist.placeholder.fade
22-
import com.google.accompanist.placeholder.placeholder
2323
import kotlinx.coroutines.ExperimentalCoroutinesApi
2424
import kotlinx.coroutines.flow.SharedFlow
25-
import st.slex.csplashscreen.R
2625
import st.slex.csplashscreen.data.model.ui.image.ImageModel
27-
import st.slex.csplashscreen.ui.core.UIResult
26+
import st.slex.csplashscreen.ui.main.UserImageHead
27+
import st.slex.csplashscreen.ui.theme.Shapes
28+
import st.slex.csplashscreen.ui.theme.Typography
2829
import java.net.URLEncoder
2930
import java.nio.charset.StandardCharsets
3031

32+
@ExperimentalMaterialApi
3133
@ExperimentalCoroutinesApi
3234
@ExperimentalCoilApi
3335
@Composable
3436
fun ImageDetailScreen(
3537
url: String,
3638
navController: NavController,
37-
imageFlow: () -> SharedFlow<UIResult<ImageModel>>
39+
imageFlow: () -> SharedFlow<ImageModel>
3840
) {
39-
val imageModel = imageFlow().collectAsState(
40-
initial = UIResult.Loading,
41-
context = rememberCoroutineScope().coroutineContext
42-
).value
43-
44-
when (imageModel) {
45-
is UIResult.Success -> BindImage(navController, imageModel.data)
46-
is UIResult.Loading -> BindImageLoading(url = url)
47-
is UIResult.Failure -> BindImageFailure(url = url)
48-
}
41+
val imageModel = imageFlow().collectAsState(initial = null).value
42+
BindDetailImage(navController, imageModel)
4943
}
5044

45+
@ExperimentalMaterialApi
5146
@ExperimentalCoilApi
5247
@Composable
53-
private fun BindImage(navController: NavController, imageModel: ImageModel) {
48+
private fun BindDetailImage(navController: NavController, imageModel: ImageModel?) {
5449
Column {
5550
Image(
5651
modifier = Modifier
@@ -59,68 +54,74 @@ private fun BindImage(navController: NavController, imageModel: ImageModel) {
5954
.clickable {
6055
val encodedUrl =
6156
URLEncoder.encode(
62-
imageModel.urls.regular,
57+
imageModel?.urls?.regular,
6358
StandardCharsets.UTF_8.toString()
6459
)
6560
navController.navigate("raw_image/$encodedUrl")
6661
},
6762
painter = rememberImagePainter(
68-
data = imageModel.urls.regular,
63+
data = imageModel?.urls?.regular,
6964
builder = {
7065
transformations(RoundedCornersTransformation())
7166
allowHardware(false)
7267
}
7368
),
7469
contentDescription = "TestImage"
7570
)
71+
72+
Spacer(modifier = Modifier.padding(4.dp))
73+
74+
UserImageHead(
75+
modifier = Modifier.padding(start = 16.dp),
76+
username = imageModel?.user?.username.toString(),
77+
url = imageModel?.user?.profile_image?.medium.toString(),
78+
navController = navController
79+
)
7680
}
77-
}
7881

79-
@ExperimentalCoilApi
80-
@Composable
81-
private fun BindImageLoading(url: String) {
82-
androidx.compose.material.Surface(
83-
modifier = Modifier
84-
.height(300.dp)
85-
.fillMaxWidth()
86-
.placeholder(
87-
visible = true,
88-
highlight = PlaceholderHighlight.fade(highlightColor = Color.LightGray),
89-
color = Color.Gray
90-
)
91-
) {}
9282

9383
}
9484

9585
@ExperimentalCoilApi
86+
@ExperimentalMaterialApi
9687
@Composable
97-
private fun BindImageFailure(url: String) {
98-
99-
Image(
88+
fun CurrentImageUser(username: String, url: String, navController: NavController) {
89+
Surface(
10090
modifier = Modifier
10191
.fillMaxWidth()
102-
.height(300.dp),
103-
painter = rememberImagePainter(
104-
data = url,
105-
builder = {
106-
transformations(
107-
RoundedCornersTransformation(),
108-
BlurTransformation(LocalContext.current)
109-
)
110-
allowHardware(false)
111-
crossfade(500)
112-
}
113-
),
114-
contentDescription = "TestImage"
115-
)
116-
Image(
117-
modifier = Modifier
118-
.fillMaxWidth()
119-
.height(300.dp),
120-
painter = rememberImagePainter(
121-
data = R.drawable.ic_baseline_sentiment_very_dissatisfied_24
122-
),
123-
contentDescription = "TestImage"
124-
)
92+
.shadow(elevation = 0.dp, Shapes.medium),
93+
onClick = {
94+
//navController.navigate("user_profile")
95+
}
96+
) {
97+
Row(
98+
modifier = Modifier
99+
.padding(8.dp),
100+
verticalAlignment = Alignment.CenterVertically
101+
) {
102+
Image(
103+
modifier = Modifier
104+
.size(48.dp)
105+
.clip(CircleShape),
106+
painter = rememberImagePainter(
107+
data = url,
108+
builder = {
109+
allowHardware(false)
110+
crossfade(500)
111+
}
112+
),
113+
contentDescription = "User Avatar"
114+
)
115+
Spacer(modifier = Modifier.padding(8.dp))
116+
Text(
117+
text = username,
118+
style = Typography.h5,
119+
maxLines = 1,
120+
lineHeight = TextUnit.Unspecified,
121+
fontFamily = FontFamily.SansSerif
122+
)
123+
}
124+
}
125+
125126
}
126127

0 commit comments

Comments
 (0)