Skip to content

Commit b5ee9d5

Browse files
authored
Merge pull request #5 from YAPP-Github/feature/NDGL-12
[NDGL-12] MVI 아키텍처 구조 설정
2 parents fe31c29 + ac2d9a9 commit b5ee9d5

16 files changed

Lines changed: 160 additions & 43 deletions

File tree

build-logic/src/main/kotlin/NDGLAndroidLibraryPlugin.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import convention.configureComposeAndroid
32
import convention.configureFirebase
43
import extensions.configureAndroidLibrary
@@ -18,9 +17,7 @@ class NDGLAndroidLibraryPlugin : Plugin<Project> {
1817
configureFirebase()
1918
configureComposeAndroid()
2019

21-
2220
dependencies {
23-
"implementation"(libs.findLibrary("lifecycle-runtime-compose").get())
2421
"implementation"(libs.findLibrary("kotlinx-immutable").get())
2522
}
2623
}

build-logic/src/main/kotlin/NDGLFeaturePlugin.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ class NDGLFeaturePlugin : Plugin<Project> {
2121
configureComposeAndroid()
2222
configureCoroutineAndroid()
2323

24-
2524
dependencies {
2625
"implementation"(project(":navigation"))
2726
"implementation"(project(":core:ui"))
@@ -31,7 +30,6 @@ class NDGLFeaturePlugin : Plugin<Project> {
3130
"implementation"(libs.findLibrary("navigation-compose").get())
3231
"implementation"(libs.findLibrary("lifecycle-viewmodel-compose").get())
3332
"implementation"(libs.findLibrary("lifecycle-runtime-compose").get())
34-
"implementation"(libs.findLibrary("kotlinx-immutable").get())
3533
"implementation"(libs.findLibrary("androidx-navigation3-runtime").get())
3634
}
3735
}

core/ui/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ android {
88

99
dependencies {
1010
implementation(libs.androidx.core.ktx)
11+
implementation(libs.kotlinx.coroutines.core)
1112
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.yapp.ui.base
2+
3+
interface UiState
4+
5+
interface UiIntent
6+
7+
interface UiSideEffect
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.yapp.ui.base
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.LaunchedEffect
5+
import androidx.compose.runtime.State
6+
import androidx.compose.runtime.getValue
7+
import androidx.compose.runtime.rememberUpdatedState
8+
import androidx.lifecycle.Lifecycle
9+
import androidx.lifecycle.ViewModel
10+
import androidx.lifecycle.compose.LocalLifecycleOwner
11+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
12+
import androidx.lifecycle.repeatOnLifecycle
13+
import androidx.lifecycle.viewModelScope
14+
import kotlinx.coroutines.Dispatchers
15+
import kotlinx.coroutines.channels.Channel
16+
import kotlinx.coroutines.channels.Channel.Factory.BUFFERED
17+
import kotlinx.coroutines.flow.MutableStateFlow
18+
import kotlinx.coroutines.flow.asStateFlow
19+
import kotlinx.coroutines.flow.receiveAsFlow
20+
import kotlinx.coroutines.launch
21+
import kotlinx.coroutines.flow.update
22+
import kotlinx.coroutines.withContext
23+
24+
abstract class BaseViewModel<S : UiState, I : UiIntent, SE : UiSideEffect>(
25+
initialState: S,
26+
) : ViewModel() {
27+
private val _state = MutableStateFlow(initialState)
28+
protected val state = _state.asStateFlow()
29+
30+
private val _sideEffect: Channel<SE> = Channel(BUFFERED)
31+
protected val sideEffect = _sideEffect.receiveAsFlow()
32+
33+
fun onIntent(intent: I) =
34+
viewModelScope.launch {
35+
handleIntent(intent)
36+
}
37+
38+
protected abstract suspend fun handleIntent(intent: I)
39+
40+
protected fun reduce(block: S.() -> S) =
41+
viewModelScope.launch {
42+
_state.update { it.block() }
43+
}
44+
45+
protected fun postSideEffect(effect: SE) =
46+
viewModelScope.launch { _sideEffect.send(effect) }
47+
48+
@Composable
49+
fun collectAsState(
50+
lifecycleState: Lifecycle.State = Lifecycle.State.STARTED,
51+
): State<S> {
52+
return state.collectAsStateWithLifecycle(minActiveState = lifecycleState)
53+
}
54+
55+
@Composable
56+
fun collectSideEffect(
57+
sideEffect: (suspend (sideEffect: SE) -> Unit),
58+
) {
59+
val sideEffectFlow = this.sideEffect
60+
val lifecycleOwner = LocalLifecycleOwner.current
61+
62+
val callback by rememberUpdatedState(newValue = sideEffect)
63+
64+
LaunchedEffect(sideEffectFlow, lifecycleOwner) {
65+
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
66+
withContext(Dispatchers.Main.immediate) {
67+
sideEffectFlow.collect { callback(it) }
68+
}
69+
}
70+
}
71+
}
72+
}

core/util/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@ plugins {
33
}
44

55
dependencies {
6-
// Coroutines
76
implementation(libs.kotlinx.coroutines.core)
87
}

feature/auth/src/main/java/com/yapp/ndgl/feature/auth/AuthScreen.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fun AuthRoute(
2121
}
2222

2323
@Composable
24-
internal fun AuthScreen(
24+
private fun AuthScreen(
2525
) {
2626
LazyColumn(
2727
modifier = Modifier.fillMaxSize(),
@@ -36,6 +36,6 @@ internal fun AuthScreen(
3636

3737
@Preview(showBackground = true)
3838
@Composable
39-
fun AuthScreenPreview() {
39+
private fun AuthScreenPreview() {
4040
AuthScreen()
41-
}
41+
}

feature/home/src/main/java/com/yapp/ndgl/feature/home/HomeScreen.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal fun HomeRoute(
2020
}
2121

2222
@Composable
23-
internal fun HomeScreen(
23+
private fun HomeScreen(
2424
) {
2525
LazyColumn(
2626
modifier = Modifier.fillMaxSize(),
@@ -35,6 +35,6 @@ internal fun HomeScreen(
3535

3636
@Preview(showBackground = true)
3737
@Composable
38-
fun HomeScreenPreview() {
38+
private fun HomeScreenPreview() {
3939
HomeScreen()
40-
}
40+
}

feature/travel-helper/src/main/java/com/yapp/ndgl/feature/travelhelper/TravelHelperScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal fun TravelHelperRoute(
1919
}
2020

2121
@Composable
22-
internal fun TravelHelperScreen(
22+
private fun TravelHelperScreen(
2323
) {
2424
LazyColumn(
2525
modifier = Modifier.fillMaxSize(),
@@ -34,6 +34,6 @@ internal fun TravelHelperScreen(
3434

3535
@Preview(showBackground = true)
3636
@Composable
37-
fun TravelHelperScreenPreview() {
37+
private fun TravelHelperScreenPreview() {
3838
TravelHelperScreen()
3939
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.yapp.ndgl.feature.travel
2+
3+
import com.yapp.ui.base.UiIntent
4+
import com.yapp.ui.base.UiSideEffect
5+
import com.yapp.ui.base.UiState
6+
7+
data class TravelState(
8+
val displayText: String = "초기 상태"
9+
) : UiState
10+
11+
12+
sealed interface TravelIntent : UiIntent {
13+
data class ClickTravel(val travelId: Int) : TravelIntent
14+
}
15+
16+
sealed interface TravelSideEffect : UiSideEffect {
17+
data class NavigateToDetail(val travelId: Int) : TravelSideEffect
18+
}

0 commit comments

Comments
 (0)