Skip to content

Commit

Permalink
refactor: Migrate to Orbit-MVI
Browse files Browse the repository at this point in the history
  • Loading branch information
kongwoojin committed Apr 1, 2024
1 parent 8633d98 commit ca573b8
Show file tree
Hide file tree
Showing 41 changed files with 618 additions and 394 deletions.
5 changes: 5 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ dependencies {
implementation(libs.firebase.messaging)
implementation(libs.accompanist.permissions)

implementation(libs.orbit.core)
implementation(libs.orbit.viewmodel)
implementation(libs.orbit.compose)


debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -29,6 +28,8 @@ import com.kongjak.koreatechboard.ui.components.HtmlText
import com.kongjak.koreatechboard.ui.theme.articleSubText
import com.kongjak.koreatechboard.ui.theme.articleTitle
import com.kongjak.koreatechboard.ui.viewmodel.ThemeViewModel
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect
import java.util.UUID

@OptIn(ExperimentalMaterial3Api::class)
Expand All @@ -39,7 +40,10 @@ fun ArticleScreen(
department: String,
uuid: UUID
) {
val uiState by articleViewModel.uiState.collectAsState()
articleViewModel.collectSideEffect {
articleViewModel.handleSideEffect(it)
}
val uiState by articleViewModel.collectAsState()

val isLoading = uiState.isLoading

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import com.kongjak.koreatechboard.ui.theme.KoreatechBoardTheme
import com.kongjak.koreatechboard.ui.viewmodel.ThemeViewModel
import com.kongjak.koreatechboard.util.findActivity
import dagger.hilt.android.AndroidEntryPoint
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect
import java.util.UUID

@AndroidEntryPoint
Expand All @@ -54,9 +56,18 @@ class ArticleActivity : ComponentActivity() {
}

@Composable
fun ArticleMain(articleViewModel: ArticleViewModel = hiltViewModel(), department: String, uuid: UUID) {
fun ArticleMain(
articleViewModel: ArticleViewModel = hiltViewModel(),
department: String,
uuid: UUID
) {
val context = LocalContext.current
Toolbar(articleViewModel = articleViewModel, context = context, department = department, uuid = uuid)
Toolbar(
articleViewModel = articleViewModel,
context = context,
department = department,
uuid = uuid
)
}

@OptIn(ExperimentalMaterial3Api::class)
Expand All @@ -69,7 +80,10 @@ fun Toolbar(
department: String,
uuid: UUID
) {
val uiState by articleViewModel.uiState.collectAsState()
articleViewModel.collectSideEffect {
articleViewModel.handleSideEffect(it)
}
val uiState by articleViewModel.collectAsState()
val articleUrl = uiState.url
val isDynamicColor by themeViewModel.isDynamicTheme.observeAsState(true)
val isDarkTheme by themeViewModel.isDarkTheme.observeAsState()
Expand Down Expand Up @@ -119,7 +133,10 @@ fun Toolbar(
}
) { contentPadding ->
Column(modifier = Modifier.padding(contentPadding)) {
val networkState by networkViewModel.uiState.collectAsState()
networkViewModel.collectSideEffect {
networkViewModel.handleSideEffect(it)
}
val networkState by networkViewModel.collectAsState()
val isNetworkConnected = networkState.isConnected
if (isNetworkConnected) {
ArticleScreen(
Expand All @@ -129,7 +146,11 @@ fun Toolbar(
uuid = uuid
)
} else {
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = stringResource(id = R.string.network_unavailable))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.kongjak.koreatechboard.ui.article

import com.kongjak.koreatechboard.ui.base.UiEvent
import java.util.UUID

sealed class ArticleEvent : UiEvent {
data class FetchData(val department: String, val uuid: UUID) : ArticleEvent()
sealed class ArticleSideEffect {
data class FetchData(val department: String, val uuid: UUID) : ArticleSideEffect()
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.kongjak.koreatechboard.ui.article

import com.kongjak.koreatechboard.domain.model.Article
import com.kongjak.koreatechboard.ui.base.UiState
import com.kongjak.koreatechboard.util.routes.Department
import java.util.UUID

Expand All @@ -15,4 +14,4 @@ data class ArticleState(
val statusCode: Int = 200,
val url: String = "",
val error: String = ""
) : UiState
)
Original file line number Diff line number Diff line change
@@ -1,65 +1,86 @@
package com.kongjak.koreatechboard.ui.article

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kongjak.koreatechboard.domain.base.ResponseResult
import com.kongjak.koreatechboard.domain.usecase.api.GetArticleUseCase
import com.kongjak.koreatechboard.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import org.orbitmvi.orbit.ContainerHost
import org.orbitmvi.orbit.syntax.simple.intent
import org.orbitmvi.orbit.syntax.simple.postSideEffect
import org.orbitmvi.orbit.syntax.simple.reduce
import org.orbitmvi.orbit.viewmodel.container
import java.util.UUID
import javax.inject.Inject

@HiltViewModel
class ArticleViewModel @Inject constructor(
private val getArticleUseCase: GetArticleUseCase
) : BaseViewModel<ArticleState, ArticleEvent>(ArticleState()) {
) : ContainerHost<ArticleState, ArticleSideEffect>, ViewModel() {

override val container = container<ArticleState, ArticleSideEffect>(ArticleState())

fun getArticleData(department: String, uuid: UUID) {
sendEvent(ArticleEvent.FetchData(department, uuid))
intent {
postSideEffect(ArticleSideEffect.FetchData(department, uuid))
}
}

override fun reduce(oldState: ArticleState, event: ArticleEvent) {
when (event) {
is ArticleEvent.FetchData -> {
fun handleSideEffect(sideEffect: ArticleSideEffect) {
when (sideEffect) {
is ArticleSideEffect.FetchData -> {
viewModelScope.launch {
setState(oldState.copy(isLoading = true, isLoaded = false))

intent {
reduce {
state.copy(
isLoading = true,
isLoaded = false
)
}
}
runCatching {
getArticleUseCase(event.uuid)
getArticleUseCase(sideEffect.uuid)
}.onSuccess {
when (it) {
is ResponseResult.Success -> {
setState(
oldState.copy(
isSuccess = true,
article = it.data,
isLoading = false,
isLoaded = true,
url = it.data.articleUrl
)
)
intent {
reduce {
state.copy(
isSuccess = true,
article = it.data,
isLoading = false,
isLoaded = true,
url = it.data.articleUrl
)
}
}
}

is ResponseResult.Error -> {
setState(
oldState.copy(
isSuccess = false,
isLoading = false,
statusCode = it.errorType.statusCode,
error = it.errorType.statusCode.toString()
)
)
intent {
reduce {
state.copy(
isSuccess = false,
isLoading = false,
statusCode = it.errorType.statusCode,
error = it.errorType.statusCode.toString()
)
}
}
}
}
}.onFailure {
setState(
oldState.copy(
isSuccess = false,
isLoading = false,
error = it.localizedMessage ?: "",
isLoaded = true
)
)
intent {
reduce {
state.copy(
isSuccess = false,
isLoading = false,
error = it.localizedMessage ?: "",
isLoaded = true
)
}
}
}
}
}
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
Expand All @@ -40,6 +39,8 @@ import com.kongjak.koreatechboard.ui.theme.KoreatechBoardTheme
import com.kongjak.koreatechboard.ui.viewmodel.ThemeViewModel
import com.kongjak.koreatechboard.util.routes.Department
import dagger.hilt.android.AndroidEntryPoint
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
Expand All @@ -63,6 +64,8 @@ class MainActivity : ComponentActivity() {
}

setContent {
mainViewModel.collectSideEffect { mainViewModel.handleSideEffect(it) }

val isDynamicColor by themeViewModel.isDynamicTheme.observeAsState(true)
val isDarkTheme by themeViewModel.isDarkTheme.observeAsState()
KoreatechBoardTheme(
Expand Down Expand Up @@ -160,7 +163,7 @@ fun BottomNavigation(navController: NavHostController) {

@Composable
fun NavigationGraph(navController: NavHostController, mainViewModel: MainViewModel) {
val uiState by mainViewModel.uiState.collectAsState()
val uiState by mainViewModel.collectAsState()
val defaultScreen = uiState.defaultScreen
val defaultDepartment = uiState.defaultDepartment
val isOpenedFromNotification = uiState.isOpenedFromNotification
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.kongjak.koreatechboard.ui.main

import com.kongjak.koreatechboard.model.BottomNavigationItem
import com.kongjak.koreatechboard.ui.base.UiEvent
import com.kongjak.koreatechboard.util.routes.Department

sealed class MainEvent : UiEvent {
data class SetDefaultScreen(val defaultScreen: BottomNavigationItem) : MainEvent()
data class SetDefaultDepartment(val defaultDepartment: Department) : MainEvent()
data object SetOpenedFromNotification : MainEvent()
sealed class MainSideEffect {
data class SetDefaultScreen(val defaultScreen: BottomNavigationItem) : MainSideEffect()
data class SetDefaultDepartment(val defaultDepartment: Department) : MainSideEffect()
data object SetOpenedFromNotification : MainSideEffect()
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.kongjak.koreatechboard.ui.main

import com.kongjak.koreatechboard.model.BottomNavigationItem
import com.kongjak.koreatechboard.ui.base.UiState
import com.kongjak.koreatechboard.util.routes.Department

data class MainState(
val defaultScreen: BottomNavigationItem = BottomNavigationItem.Home,
val defaultDepartment: Department? = null,
val isOpenedFromNotification: Boolean = false
) : UiState
)
Loading

0 comments on commit ca573b8

Please sign in to comment.