From 4870b7377a526f712585c79698a401ea50412805 Mon Sep 17 00:00:00 2001 From: klaus Date: Tue, 2 Jun 2026 13:29:01 +0900 Subject: [PATCH] =?UTF-8?q?feat(home):=20=ED=99=88=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?ViewModel=EC=9D=84=20=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 2 + .../main/home/HomeRecommendationViewModel.kt | 126 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 1c50aaf3..aa0e2722 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -177,6 +177,7 @@ import kr.co.vividnext.sodalive.user.find_password.FindPasswordViewModel import kr.co.vividnext.sodalive.user.login.LoginViewModel import kr.co.vividnext.sodalive.user.signup.SignUpViewModel import kr.co.vividnext.sodalive.v2.main.MainV2ViewModel +import kr.co.vividnext.sodalive.v2.main.home.HomeRecommendationViewModel import kr.co.vividnext.sodalive.v2.main.home.data.HomeRecommendationApi import kr.co.vividnext.sodalive.v2.main.home.data.HomeRecommendationRepository import okhttp3.OkHttpClient @@ -384,6 +385,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { SearchViewModel(get()) } viewModel { PointStatusViewModel(get()) } viewModel { HomeViewModel(get(), get()) } + viewModel { HomeRecommendationViewModel(get()) } viewModel { PushNotificationListViewModel(get()) } viewModel { CharacterTabViewModel(get()) } viewModel { CharacterDetailViewModel(get()) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt new file mode 100644 index 00000000..7475d054 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt @@ -0,0 +1,126 @@ +package kr.co.vividnext.sodalive.v2.main.home + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.common.ToastMessage +import kr.co.vividnext.sodalive.v2.main.home.data.FollowRecommendedCreatorsRequest +import kr.co.vividnext.sodalive.v2.main.home.data.HomeRecommendationRepository +import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationGenreCreatorSection +import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationUiState +import kr.co.vividnext.sodalive.v2.main.home.model.toContent + +class HomeRecommendationViewModel( + private val repository: HomeRecommendationRepository +) : BaseViewModel() { + + private val _recommendationStateLiveData = MutableLiveData() + val recommendationStateLiveData: LiveData + get() = _recommendationStateLiveData + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private val _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + fun loadRecommendations() { + _isLoading.value = true + _recommendationStateLiveData.value = HomeRecommendationUiState.Loading + + compositeDisposable.add( + repository.getRecommendations(token = authToken()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + val data = it.data + if (it.success && data != null) { + val content = data.toContent() + _recommendationStateLiveData.value = if (content.isEmpty) { + HomeRecommendationUiState.Empty + } else { + content + } + } else { + showUnknownError(it.message) + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + showUnknownError(it.message) + } + ) + ) + } + + fun followCreators(sectionKey: String, creatorIds: List) { + if (creatorIds.isEmpty()) return + + _isLoading.value = true + compositeDisposable.add( + repository.followRecommendedCreators( + request = FollowRecommendedCreatorsRequest(creatorIds = creatorIds), + token = authToken() + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success) { + updateFollowCompleted(sectionKey) + } else { + showUnknownError(it.message) + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + showUnknownError(it.message) + } + ) + ) + } + + private fun updateFollowCompleted(sectionKey: String) { + val content = _recommendationStateLiveData.value as? HomeRecommendationUiState.Content ?: return + _recommendationStateLiveData.value = when (sectionKey) { + SECTION_KEY_CHEER_CREATORS -> content.copy( + cheerCreators = content.cheerCreators.copy(isFollowCompleted = true) + ) + else -> content.copy( + genreCreators = HomeRecommendationGenreCreatorSection( + groups = content.genreCreators.groups.map { group -> + if (group.genre == sectionKey || sectionKey == "$SECTION_KEY_GENRE_CREATORS:${group.genre}") { + group.copy(isFollowCompleted = true) + } else { + group + } + } + ) + ) + } + } + + private fun showUnknownError(message: String?) { + _recommendationStateLiveData.value = HomeRecommendationUiState.Error(message = message) + _toastLiveData.postValue(ToastMessage(resId = R.string.common_error_unknown)) + } + + private fun authToken(): String = "Bearer ${SharedPreferenceManager.token}" + + private companion object { + const val SECTION_KEY_CHEER_CREATORS = "cheerCreators" + const val SECTION_KEY_GENRE_CREATORS = "genreCreators" + } +}