Skip to content

Commit 5b2d29d

Browse files
authored
Merge pull request #85 from stslex/dev
Dev
2 parents a60bf72 + cfc7e95 commit 5b2d29d

File tree

11 files changed

+170
-90
lines changed

11 files changed

+170
-90
lines changed
Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package st.slex.csplashscreen.ui.components
22

3+
import androidx.compose.animation.ExperimentalSharedTransitionApi
4+
import androidx.compose.animation.SharedTransitionLayout
35
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.CompositionLocalProvider
47
import androidx.compose.runtime.Stable
58
import androidx.compose.ui.Modifier
69
import androidx.navigation.NavHostController
710
import androidx.navigation.compose.NavHost
811
import st.slex.csplashscreen.core.navigation.Screen
12+
import st.slex.csplashscreen.core.ui.theme.LocalSharedTransitionScope
913
import st.slex.csplashscreen.feature.collection.navigation.singleCollectionGraph
1014
import st.slex.csplashscreen.feature.favourite.navigation.favouriteGraph
1115
import st.slex.csplashscreen.feature.feature_photo_detail.navigation.imageDetailGraph
@@ -16,22 +20,27 @@ import st.slex.csplashscreen.feature.user.navigation.userGraph
1620
@Stable
1721
class NavHostControllerHolder(val navController: NavHostController)
1822

23+
@OptIn(ExperimentalSharedTransitionApi::class)
1924
@Composable
2025
@Stable
2126
fun NavigationHost(
2227
holder: NavHostControllerHolder,
2328
modifier: Modifier = Modifier,
2429
startDestination: Screen = Screen.Home
2530
) {
26-
NavHost(
27-
navController = holder.navController,
28-
startDestination = startDestination
29-
) {
30-
homeGraph(modifier)
31-
userGraph(modifier)
32-
imageDetailGraph(modifier)
33-
searchPhotosGraph(modifier)
34-
singleCollectionGraph(modifier)
35-
favouriteGraph(modifier)
31+
SharedTransitionLayout {
32+
CompositionLocalProvider(LocalSharedTransitionScope provides this) {
33+
NavHost(
34+
navController = holder.navController,
35+
startDestination = startDestination
36+
) {
37+
homeGraph(modifier)
38+
userGraph(modifier)
39+
imageDetailGraph(modifier)
40+
searchPhotosGraph(modifier)
41+
singleCollectionGraph(modifier)
42+
favouriteGraph(modifier)
43+
}
44+
}
3645
}
3746
}

core/navigation/src/main/java/st/slex/csplashscreen/core/navigation/Screen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package st.slex.csplashscreen.core.navigation
22

3+
import androidx.compose.animation.AnimatedContentScope
34
import androidx.compose.runtime.Composable
45
import androidx.compose.runtime.Stable
56
import androidx.navigation.NavGraphBuilder
@@ -32,7 +33,7 @@ sealed interface Screen {
3233
}
3334

3435
inline fun <reified S : Screen> NavGraphBuilder.navScreen(
35-
noinline content: @Composable (S) -> Unit
36+
noinline content: @Composable AnimatedContentScope.(S) -> Unit
3637
) {
3738
composable<S> { backStackEntry ->
3839
content(backStackEntry.toRoute())
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
package st.slex.csplashscreen.core.ui.base
22

33
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.CompositionLocalProvider
45
import androidx.lifecycle.ViewModel
56
import androidx.navigation.NavGraphBuilder
67
import org.koin.androidx.compose.koinViewModel
78
import st.slex.csplashscreen.core.navigation.Screen
89
import st.slex.csplashscreen.core.navigation.navScreen
10+
import st.slex.csplashscreen.core.ui.theme.LocalNavAnimatedVisibilityScope
911

1012
inline fun <reified Destination : Screen, reified S : ViewModel> NavGraphBuilder.screen(
1113
noinline content: @Composable (Destination, S) -> Unit
1214
) {
1315
navScreen<Destination> { screen ->
14-
val viewModel: S = koinViewModel(
15-
key = screen.hashCode().toString()
16-
)
17-
/*TODO maybe good point to make instance of state, event, action here
18-
and then send in to Content Screen*/
19-
content(screen, viewModel)
16+
CompositionLocalProvider(LocalNavAnimatedVisibilityScope provides this) {
17+
val viewModel: S = koinViewModel(
18+
key = screen.hashCode().toString()
19+
)
20+
/*TODO maybe good point to make instance of state, event, action here
21+
and then send in to Content Screen*/
22+
content(screen, viewModel)
23+
}
2024
}
2125
}
Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package st.slex.csplashscreen.core.ui.components.base
22

3+
import androidx.compose.animation.ExperimentalSharedTransitionApi
4+
import androidx.compose.animation.SharedTransitionScope
5+
import androidx.compose.animation.fadeIn
6+
import androidx.compose.animation.fadeOut
37
import androidx.compose.foundation.background
48
import androidx.compose.foundation.clickable
5-
import androidx.compose.foundation.interaction.MutableInteractionSource
69
import androidx.compose.foundation.layout.Box
710
import androidx.compose.foundation.layout.BoxScope
811
import androidx.compose.foundation.layout.Row
@@ -12,7 +15,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
1215
import androidx.compose.foundation.layout.height
1316
import androidx.compose.foundation.layout.padding
1417
import androidx.compose.foundation.shape.RoundedCornerShape
15-
import androidx.compose.material.ripple.rememberRipple
1618
import androidx.compose.material3.MaterialTheme
1719
import androidx.compose.runtime.Composable
1820
import androidx.compose.runtime.remember
@@ -21,11 +23,13 @@ import androidx.compose.ui.Modifier
2123
import androidx.compose.ui.draw.clip
2224
import androidx.compose.ui.layout.ContentScale
2325
import androidx.compose.ui.platform.LocalConfiguration
24-
import androidx.compose.ui.semantics.Role
2526
import androidx.compose.ui.unit.dp
2627
import st.slex.csplashscreen.core.ui.components.ImageComponent
2728
import st.slex.csplashscreen.core.ui.theme.Dimen
29+
import st.slex.csplashscreen.core.ui.theme.rememberNavAnimatedVisibilityScope
30+
import st.slex.csplashscreen.core.ui.theme.rememberSharedTransitionScope
2831

32+
@OptIn(ExperimentalSharedTransitionApi::class)
2933
@Composable
3034
fun PhotosBaseItem(
3135
onContainerClick: () -> Unit,
@@ -39,44 +43,58 @@ fun PhotosBaseItem(
3943
val itemHeight = remember {
4044
configuration.screenHeightDp.dp / 3
4145
}
42-
Box(
43-
modifier = modifier
44-
.fillMaxWidth()
45-
.height(itemHeight)
46-
.padding(bottom = Dimen.medium)
47-
.clip(RoundedCornerShape(Dimen.medium))
48-
.clickable(
49-
onClick = onContainerClick,
50-
role = Role.Button,
51-
interactionSource = remember { MutableInteractionSource() },
52-
indication = rememberRipple()
53-
),
54-
) {
55-
ImageComponent(
56-
modifier = Modifier.fillMaxSize(),
57-
url = url,
58-
contentScale = ContentScale.Crop
59-
)
60-
Row(
46+
val sharedTransitionScope = rememberSharedTransitionScope()
47+
with(sharedTransitionScope) {
48+
Box(
6149
modifier = Modifier
62-
.align(Alignment.TopStart)
50+
.sharedBounds(
51+
sharedContentState = rememberSharedContentState(key = url),
52+
animatedVisibilityScope = rememberNavAnimatedVisibilityScope(),
53+
enter = fadeIn(),
54+
exit = fadeOut(),
55+
resizeMode = SharedTransitionScope.ResizeMode.ScaleToBounds(),
56+
)
57+
.then(modifier)
6358
.fillMaxWidth()
59+
.height(itemHeight)
60+
.padding(bottom = Dimen.medium)
61+
.clip(RoundedCornerShape(Dimen.medium))
6462
.clickable(
65-
onClick = onHeaderClick,
66-
role = Role.Button,
67-
interactionSource = remember { MutableInteractionSource() },
68-
indication = rememberRipple()
69-
)
70-
.background(
71-
color = MaterialTheme.colorScheme.background.copy(
72-
alpha = 0.7f
73-
)
74-
)
75-
.padding(Dimen.small),
76-
verticalAlignment = Alignment.CenterVertically
63+
onClick = onContainerClick,
64+
// todo check this if needed
65+
// role = Role.Button,
66+
// interactionSource = remember { MutableInteractionSource() },
67+
// indication = rememberRipple()
68+
),
7769
) {
78-
headerContent()
70+
ImageComponent(
71+
modifier = Modifier
72+
.fillMaxSize(),
73+
url = url,
74+
contentScale = ContentScale.Crop
75+
)
76+
Row(
77+
modifier = Modifier
78+
.align(Alignment.TopStart)
79+
.fillMaxWidth()
80+
.clickable(
81+
onClick = onHeaderClick,
82+
// todo check this if needed
83+
// role = Role.Button,
84+
// interactionSource = remember { MutableInteractionSource() },
85+
// indication = rememberRipple()
86+
)
87+
.background(
88+
color = MaterialTheme.colorScheme.background.copy(
89+
alpha = 0.7f
90+
)
91+
)
92+
.padding(Dimen.small),
93+
verticalAlignment = Alignment.CenterVertically
94+
) {
95+
headerContent()
96+
}
97+
content()
7998
}
80-
content()
8199
}
82100
}

core/ui/src/main/java/st/slex/csplashscreen/core/ui/theme/Theme.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package st.slex.csplashscreen.core.ui.theme
22

33
import android.os.Build
4+
import androidx.compose.animation.AnimatedVisibilityScope
5+
import androidx.compose.animation.ExperimentalSharedTransitionApi
6+
import androidx.compose.animation.SharedTransitionScope
47
import androidx.compose.foundation.isSystemInDarkTheme
58
import androidx.compose.material3.MaterialTheme
69
import androidx.compose.material3.darkColorScheme
@@ -9,6 +12,7 @@ import androidx.compose.material3.dynamicLightColorScheme
912
import androidx.compose.material3.lightColorScheme
1013
import androidx.compose.runtime.Composable
1114
import androidx.compose.runtime.CompositionLocalProvider
15+
import androidx.compose.runtime.compositionLocalOf
1216
import androidx.compose.ui.graphics.Color
1317
import androidx.compose.ui.platform.LocalContext
1418
import androidx.compose.ui.unit.dp
@@ -184,4 +188,17 @@ fun AppTheme(
184188
content = content
185189
)
186190
}
187-
}
191+
}
192+
193+
val LocalNavAnimatedVisibilityScope = compositionLocalOf<AnimatedVisibilityScope?> { null }
194+
195+
@OptIn(ExperimentalSharedTransitionApi::class)
196+
val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope?> { null }
197+
198+
@OptIn(ExperimentalSharedTransitionApi::class)
199+
@Composable
200+
fun rememberSharedTransitionScope() = checkNotNull(LocalSharedTransitionScope.current)
201+
202+
@Composable
203+
fun rememberNavAnimatedVisibilityScope() =
204+
checkNotNull(LocalNavAnimatedVisibilityScope.current)

fastlane/report.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55

66

77

8-
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000267292">
8+
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000270334">
99

1010
</testcase>
1111

1212

13-
<testcase classname="fastlane.lanes" name="1: clean bundle" time="321.668838943">
13+
<testcase classname="fastlane.lanes" name="1: clean bundle" time="336.868891465">
1414

1515
</testcase>
1616

1717

18-
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="27.945471862">
18+
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="24.176761763">
1919

2020
</testcase>
2121

feature/collection/src/main/java/st/slex/csplashscreen/feature/collection/ui/presenter/SingleCollectionStore.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.StateFlow
99
import kotlinx.coroutines.flow.filter
1010
import kotlinx.coroutines.flow.flatMapLatest
1111
import kotlinx.coroutines.flow.map
12+
import st.slex.csplashscreen.core.core.Logger
1213
import st.slex.csplashscreen.core.core.coroutine.AppDispatcher
1314
import st.slex.csplashscreen.core.core.coroutine.CoroutineExt.mapState
1415
import st.slex.csplashscreen.core.photos.ui.model.PhotoModel
@@ -33,6 +34,7 @@ class SingleCollectionStore(
3334
) {
3435

3536
override fun sendAction(action: Action) {
37+
Logger.d("action: $action", TAG)
3638
when (action) {
3739
is Action.Init -> actionInit(action)
3840
is Action.OnProfileClick -> actionProfileClick(action)
@@ -46,7 +48,7 @@ class SingleCollectionStore(
4648
collectionId = action.collectionId
4749
)
4850
}
49-
allPhotos.launch { pagingData ->
51+
getPhotos(action.collectionId).launch { pagingData ->
5052
updateState { currentState ->
5153
currentState.copy(
5254
photos = pagingData
@@ -55,6 +57,18 @@ class SingleCollectionStore(
5557
}
5658
}
5759

60+
private fun getPhotos(
61+
collectionId: String
62+
): StateFlow<PagingData<PhotoModel>> = Pager(pagingConfig) {
63+
PagingSource { page, pageSize ->
64+
interactor.getPhotos(
65+
uuid = collectionId,
66+
page = page,
67+
pageSize = pageSize
68+
).map { it.toPresentation() }
69+
}
70+
}.flow.state()
71+
5872
@OptIn(ExperimentalCoroutinesApi::class)
5973
private val allPhotos: StateFlow<PagingData<PhotoModel>>
6074
get() = state
@@ -90,5 +104,6 @@ class SingleCollectionStore(
90104
pageSize = 5,
91105
enablePlaceholders = false
92106
)
107+
private const val TAG = "SingleCollectionStore"
93108
}
94109
}

feature/home/src/main/java/st/slex/csplashscreen/feature/home/ui/MainScreen.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package st.slex.csplashscreen.feature.home.ui
22

3-
import androidx.compose.foundation.ExperimentalFoundationApi
43
import androidx.compose.foundation.layout.Column
54
import androidx.compose.foundation.lazy.rememberLazyListState
65
import androidx.compose.foundation.pager.HorizontalPager
@@ -17,7 +16,6 @@ import st.slex.csplashscreen.core.photos.ui.model.PhotoModel
1716
import st.slex.csplashscreen.feature.home.ui.component.tabs.MainScreenTabRow
1817
import st.slex.csplashscreen.feature.home.ui.component.tabs.MainScreenTabs
1918

20-
@OptIn(ExperimentalFoundationApi::class)
2119
@Composable
2220
fun MainScreen(
2321
navToProfile: (username: String) -> Unit,

feature/home/src/main/java/st/slex/csplashscreen/feature/home/ui/presenter/HomeStore.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ class HomeStore(
2828
initialState = State.INIT
2929
) {
3030

31-
private val collections: StateFlow<PagingData<CollectionModel>>
32-
get() = Pager(config = config) { PagingSource(interactor::getAllCollections) }
31+
private val collections: StateFlow<PagingData<CollectionModel>> =
32+
Pager(config = config) { PagingSource(interactor::getAllCollections) }
3333
.state { collection -> collection.toPresentation() }
3434

35-
private val photos: StateFlow<PagingData<PhotoModel>>
36-
get() = Pager(config = config) { PagingSource(interactor::getAllPhotos) }
35+
private val photos: StateFlow<PagingData<PhotoModel>> =
36+
Pager(config = config) { PagingSource(interactor::getAllPhotos) }
3737
.state { image -> image.toPresentation() }
3838

3939
override fun sendAction(action: Action) {

0 commit comments

Comments
 (0)