Skip to content

Commit

Permalink
Merge pull request #40 from kongwoojin/feature/mvi-migration
Browse files Browse the repository at this point in the history
Migrate to MVI architecture
  • Loading branch information
kongwoojin authored Mar 18, 2024
2 parents a257368 + 023b885 commit 445c324
Show file tree
Hide file tree
Showing 33 changed files with 544 additions and 367 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.material3.Scaffold
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.Alignment
Expand All @@ -32,9 +33,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.kongjak.koreatechboard.R
import com.kongjak.koreatechboard.ui.article.ArticleScreen
import com.kongjak.koreatechboard.ui.article.ArticleViewModel
import com.kongjak.koreatechboard.ui.state.NetworkState
import com.kongjak.koreatechboard.ui.network.NetworkViewModel
import com.kongjak.koreatechboard.ui.theme.KoreatechBoardTheme
import com.kongjak.koreatechboard.ui.viewmodel.NetworkViewModel
import com.kongjak.koreatechboard.ui.viewmodel.ThemeViewModel
import com.kongjak.koreatechboard.util.findActivity
import dagger.hilt.android.AndroidEntryPoint
Expand All @@ -47,18 +47,18 @@ class ArticleActivity : ComponentActivity() {
super.onCreate(savedInstanceState)

val uuid = UUID.fromString(intent.getStringExtra("uuid"))
val site = intent.getStringExtra("site")!!
val department = intent.getStringExtra("site")!!

setContent {
ArticleMain(site = site, uuid = uuid)
ArticleMain(department = department, uuid = uuid)
}
}
}

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

@OptIn(ExperimentalMaterial3Api::class)
Expand All @@ -68,10 +68,11 @@ fun Toolbar(
themeViewModel: ThemeViewModel = hiltViewModel(),
networkViewModel: NetworkViewModel = hiltViewModel(),
context: Context,
site: String,
department: String,
uuid: UUID
) {
val articleUrl by articleViewModel.url.observeAsState()
val uiState by articleViewModel.uiState.collectAsState()
val articleUrl = uiState.url
val isDynamicColor by themeViewModel.isDynamicTheme.observeAsState(true)
val isDarkTheme by themeViewModel.isDarkTheme.observeAsState()

Expand Down Expand Up @@ -120,12 +121,13 @@ fun Toolbar(
}
) { contentPadding ->
Column(modifier = Modifier.padding(contentPadding)) {
val networkState by networkViewModel.networkState.observeAsState()
if (networkState == NetworkState.Connected) {
val networkState by networkViewModel.uiState.collectAsState()
val isNetworkConnected = networkState.isConnected
if (isNetworkConnected) {
ArticleScreen(
articleViewModel = articleViewModel,
themeViewModel = themeViewModel,
site = site,
department = department,
uuid = uuid
)
} else {
Expand Down
20 changes: 10 additions & 10 deletions app/src/main/java/com/kongjak/koreatechboard/ui/article/Article.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import androidx.compose.material3.pullrefresh.pullRefresh
import androidx.compose.material3.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -40,23 +40,23 @@ import java.util.UUID
fun ArticleScreen(
articleViewModel: ArticleViewModel,
themeViewModel: ThemeViewModel,
site: String,
department: String,
uuid: UUID
) {
val isLoading by articleViewModel.isLoading.observeAsState(false)
val uiState by articleViewModel.uiState.collectAsState()

val isLoading = uiState.isLoading

val pullRefreshState =
rememberPullRefreshState(isLoading, { articleViewModel.getArticleData() })
rememberPullRefreshState(isLoading, { articleViewModel.getArticleData(department, uuid) })

val data by articleViewModel.article.observeAsState()
val data = uiState.article
val context = LocalContext.current
val contentTextView = remember { TextView(context) }
val filesTextView = remember { TextView(context) }

LaunchedEffect(key1 = Unit) {
articleViewModel.setSiteData(site)
articleViewModel.setUUIDData(uuid)
articleViewModel.getArticleData()
articleViewModel.getArticleData(department, uuid)
}

Box(
Expand Down Expand Up @@ -114,7 +114,7 @@ fun ArticleScreen(
.padding(16.dp)
.fillMaxSize(),
update = {
it.htmlText = data?.content
it.htmlText = data.content
it.textSize = 16F
it.autoLinkMask = 0x0f
it.setTextColor(textColor.toArgb())
Expand All @@ -126,7 +126,7 @@ fun ArticleScreen(
modifier = Modifier
.padding(16.dp),
update = {
it.fileText = data?.files
it.fileText = data.files
it.setTextColor(textColor.toArgb())
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
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()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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

data class ArticleState(
val isLoading: Boolean = false,
val isLoaded: Boolean = false,
val isSuccess: Boolean = false,
val article: Article? = null,
val uuid: UUID = UUID.randomUUID(),
val department: String = Department.School.name,
val statusCode: Int = 200,
val url: String = "",
val error: String = ""
) : UiState
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package com.kongjak.koreatechboard.ui.article

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kongjak.koreatechboard.domain.base.ResponseResult
import com.kongjak.koreatechboard.domain.model.Article
import com.kongjak.koreatechboard.domain.usecase.GetArticleUseCase
import com.kongjak.koreatechboard.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.util.UUID
Expand All @@ -15,62 +12,56 @@ import javax.inject.Inject
@HiltViewModel
class ArticleViewModel @Inject constructor(
private val getArticleUseCase: GetArticleUseCase
) :
ViewModel() {
private val _article = MutableLiveData<Article>()
val article: LiveData<Article>
get() = _article
) : BaseViewModel<ArticleState, ArticleEvent>(ArticleState()) {

private val _uuid = MutableLiveData<UUID>()
val uuid: LiveData<UUID>
get() = _uuid

private val _site = MutableLiveData<String>()
val site: LiveData<String>
get() = _site

private val _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading

private val _statusCode = MutableLiveData(200)
val statusCode: LiveData<Int>
get() = _statusCode

private val _url = MutableLiveData<String?>()
val url: LiveData<String?>
get() = _url

fun setUUIDData(uuid: UUID) {
_uuid.value = uuid
fun getArticleData(department: String, uuid: UUID) {
sendEvent(ArticleEvent.FetchData(department, uuid))
}

fun setSiteData(site: String) {
_site.value = site
}

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

runCatching {
getArticleUseCase(uuid.value!!)
}.onSuccess {
when (it) {
is ResponseResult.Success -> {
_article.value = it.data!!
_statusCode.value = it.data.statusCode
_url.value = it.data.articleUrl
}
runCatching {
getArticleUseCase(event.uuid)
}.onSuccess {
when (it) {
is ResponseResult.Success -> {
setState(
oldState.copy(
isSuccess = true,
article = it.data,
isLoading = false,
isLoaded = true
)
)
}

is ResponseResult.Error -> {
_statusCode.value = it.errorType.statusCode
is ResponseResult.Error -> {
setState(
oldState.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
)
)
}
}
}.onFailure {
}

_isLoading.value = false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.kongjak.koreatechboard.ui.base

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

open class BaseViewModel<S : UiState, E : UiEvent>(initialState: S) : ViewModel() {
private val _uiState = MutableStateFlow(initialState)
val uiState get() = _uiState.asStateFlow()

fun sendEvent(event: E) {
reduce(_uiState.value, event)
}

fun setState(newState: S) {
_uiState.value = newState
}

open fun reduce(oldState: S, event: E) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.kongjak.koreatechboard.ui.base

interface UiEvent
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.kongjak.koreatechboard.ui.base

interface UiState
Loading

0 comments on commit 445c324

Please sign in to comment.