diff --git a/app/src/main/java/com/velogandroid/di/RetrofitModule.kt b/app/src/main/java/com/velogandroid/di/RetrofitModule.kt index cef7175..63d4e75 100644 --- a/app/src/main/java/com/velogandroid/di/RetrofitModule.kt +++ b/app/src/main/java/com/velogandroid/di/RetrofitModule.kt @@ -39,10 +39,12 @@ object RetrofitModule { @Provides @Singleton fun provideDataStore(DataStore: TokenImpl): SharedPreferenceToken = DataStore + @Provides @Singleton @Token fun provideAuthInterceptor(interceptor: TokenInterceptor): Interceptor = interceptor + @Provides @Singleton fun provideLoggingInterceptor(): HttpLoggingInterceptor { diff --git a/buildSrc/src/main/java/Configuration.kt b/buildSrc/src/main/java/Configuration.kt index bc13668..cabf44b 100644 --- a/buildSrc/src/main/java/Configuration.kt +++ b/buildSrc/src/main/java/Configuration.kt @@ -4,5 +4,5 @@ object Configuration { const val TARGET_SDK = 33 const val MIN_SDK = 26 const val VERSION_CODE = 1 - const val VERSION_NAME = "1.0" + const val VERSION_NAME = "1.0.0" } \ No newline at end of file diff --git a/core-ui/src/main/java/com/velogm/core_ui/base/BindingDialogFragment.kt b/core-ui/src/main/java/com/velogm/core_ui/base/BindingDialogFragment.kt index 16c1df7..2733a70 100644 --- a/core-ui/src/main/java/com/velogm/core_ui/base/BindingDialogFragment.kt +++ b/core-ui/src/main/java/com/velogm/core_ui/base/BindingDialogFragment.kt @@ -41,7 +41,7 @@ abstract class BindingDialogFragment( } override fun onDestroyView() { - super.onDestroyView() _binding = null + super.onDestroyView() } } \ No newline at end of file diff --git a/core-ui/src/main/java/com/velogm/core_ui/base/BindingFragment.kt b/core-ui/src/main/java/com/velogm/core_ui/base/BindingFragment.kt index a1354d5..2df0a8f 100644 --- a/core-ui/src/main/java/com/velogm/core_ui/base/BindingFragment.kt +++ b/core-ui/src/main/java/com/velogm/core_ui/base/BindingFragment.kt @@ -29,7 +29,7 @@ abstract class BindingFragment( override fun onDestroyView() { - super.onDestroyView() _binding = null + super.onDestroyView() } } \ No newline at end of file diff --git a/data-local/src/main/java/com/velogm/data_local/datasource/TokenImpl.kt b/data-local/src/main/java/com/velogm/data_local/datasource/TokenImpl.kt index 1fe7b28..e111c45 100644 --- a/data-local/src/main/java/com/velogm/data_local/datasource/TokenImpl.kt +++ b/data-local/src/main/java/com/velogm/data_local/datasource/TokenImpl.kt @@ -11,4 +11,9 @@ class TokenImpl @Inject constructor( override var token: String get() = prefs.getString("AccessToken", "")?:"" set(value) = prefs.edit { putString("AccessToken", value) } + + override var checkLogin : Boolean + get() = prefs.getBoolean("CheckLogin",false) + set(value) = prefs.edit{putBoolean("CheckLogin",value)} + } \ No newline at end of file diff --git a/data/src/main/java/com/velogm/data/repositoryimpl/AuthRepositoryImpl.kt b/data/src/main/java/com/velogm/data/repositoryimpl/AuthRepositoryImpl.kt index bb6ac57..7f2cff8 100644 --- a/data/src/main/java/com/velogm/data/repositoryimpl/AuthRepositoryImpl.kt +++ b/data/src/main/java/com/velogm/data/repositoryimpl/AuthRepositoryImpl.kt @@ -1,26 +1,18 @@ package com.velogm.data.repositoryimpl -import com.velogm.data.datasource.SharedPreferencesDataSource +import com.velogm.data_local.datasource.TokenImpl import com.velogm.domain.repository.AuthRepository import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( - private val sharedPrefDataSource: SharedPreferencesDataSource + private val sharedPrefDataSource: TokenImpl ) : AuthRepository { override fun saveAccessToken(a: String) { - sharedPrefDataSource.accessToken = a + sharedPrefDataSource.token = a } override fun getAccessToken(): String { - return sharedPrefDataSource.accessToken ?: "" - } - - override fun saveRefreshToken(b: String) { - sharedPrefDataSource.refreshToken = b - } - - override fun getRefreshToken(): String { - return sharedPrefDataSource.refreshToken ?: "" + return sharedPrefDataSource.token ?: "" } override fun checkLogin(): Boolean { @@ -32,11 +24,11 @@ class AuthRepositoryImpl @Inject constructor( } override fun getWithdrawal(): Boolean { - return sharedPrefDataSource.withdrawal + return sharedPrefDataSource.checkLogin } override fun saveWithdrawal(checkWithdrawal: Boolean) { - sharedPrefDataSource.withdrawal = checkWithdrawal + sharedPrefDataSource.checkLogin = checkWithdrawal } diff --git a/data/src/main/java/com/velogm/data/repositoryimpl/TagRepositoryImpl.kt b/data/src/main/java/com/velogm/data/repositoryimpl/TagRepositoryImpl.kt index 18becd7..78f2d6b 100644 --- a/data/src/main/java/com/velogm/data/repositoryimpl/TagRepositoryImpl.kt +++ b/data/src/main/java/com/velogm/data/repositoryimpl/TagRepositoryImpl.kt @@ -1,24 +1,30 @@ package com.velogm.data.repositoryimpl import com.velogm.data.datasource.TagDataSource +import com.velogm.domain.OutResult import com.velogm.domain.model.PostList import com.velogm.domain.model.Tag import com.velogm.domain.repository.TagRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import retrofit2.HttpException +import java.lang.RuntimeException import javax.inject.Inject class TagRepositoryImpl @Inject constructor( private val dataSource: TagDataSource ) : TagRepository { - override suspend fun getTag(): Flow> { - return flow { - val result = kotlin.runCatching { - dataSource.getTag().map { Tag(it) } - } - emit(result.getOrDefault(emptyList())) + override suspend fun getTag(): Flow>> = flow { + val result = runCatching { + val tag = dataSource.getTag().map { Tag(it) } + OutResult.Success(tag) + } + val outcome = result.getOrElse { + val errorCode = (it as? HttpException)?.code() ?: -1 + OutResult.Failure(error = VelogHttpException(errorCode, "$errorCode")) } + emit(outcome) } override suspend fun getPopularTag(): Flow> { @@ -56,4 +62,9 @@ class TagRepositoryImpl @Inject constructor( emit(result.getOrDefault(PostList(emptyList()))) } } -} \ No newline at end of file +} + +class VelogHttpException( + val httpCode: Int, + override val message: String, +) : RuntimeException() \ No newline at end of file diff --git a/domain/src/main/java/com/velogm/domain/NetworkErrorHandling.kt b/domain/src/main/java/com/velogm/domain/NetworkErrorHandling.kt new file mode 100644 index 0000000..5ad539c --- /dev/null +++ b/domain/src/main/java/com/velogm/domain/NetworkErrorHandling.kt @@ -0,0 +1,7 @@ +package com.velogm.domain + +sealed class NetworkErrorHandling { + object Unauthorized : NetworkErrorHandling() + object ServerError : NetworkErrorHandling() + object OtherError : NetworkErrorHandling() +} \ No newline at end of file diff --git a/domain/src/main/java/com/velogm/domain/OutResult.kt b/domain/src/main/java/com/velogm/domain/OutResult.kt new file mode 100644 index 0000000..ad84ec0 --- /dev/null +++ b/domain/src/main/java/com/velogm/domain/OutResult.kt @@ -0,0 +1,20 @@ +package com.velogm.domain + +import kotlinx.coroutines.flow.Flow + +sealed class OutResult { + data class Success(val data: T) : OutResult() + data class Failure(val error: Throwable?) : OutResult() +} + +suspend fun Flow>.collectOutResult( + handleSuccess: (OutResult.Success) -> Unit = {}, + handleFail: (OutResult.Failure) -> Unit = {}, +) { + collect { outcome -> + when (outcome) { + is OutResult.Success -> handleSuccess(outcome) + is OutResult.Failure -> handleFail(outcome) + } + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/velogm/domain/SharedPreferenceToken.kt b/domain/src/main/java/com/velogm/domain/SharedPreferenceToken.kt index 508193c..981814b 100644 --- a/domain/src/main/java/com/velogm/domain/SharedPreferenceToken.kt +++ b/domain/src/main/java/com/velogm/domain/SharedPreferenceToken.kt @@ -2,4 +2,5 @@ package com.velogm.domain interface SharedPreferenceToken { var token:String + var checkLogin: Boolean } \ No newline at end of file diff --git a/domain/src/main/java/com/velogm/domain/repository/AuthRepository.kt b/domain/src/main/java/com/velogm/domain/repository/AuthRepository.kt index 07b0a97..a3acd29 100644 --- a/domain/src/main/java/com/velogm/domain/repository/AuthRepository.kt +++ b/domain/src/main/java/com/velogm/domain/repository/AuthRepository.kt @@ -3,8 +3,6 @@ package com.velogm.domain.repository interface AuthRepository { fun saveAccessToken(a: String) fun getAccessToken(): String - fun saveRefreshToken(b: String) - fun getRefreshToken(): String fun checkLogin(): Boolean fun saveCheckLogin(checkLogin: Boolean) fun getWithdrawal(): Boolean diff --git a/domain/src/main/java/com/velogm/domain/repository/TagRepository.kt b/domain/src/main/java/com/velogm/domain/repository/TagRepository.kt index 616772c..4e208d4 100644 --- a/domain/src/main/java/com/velogm/domain/repository/TagRepository.kt +++ b/domain/src/main/java/com/velogm/domain/repository/TagRepository.kt @@ -1,11 +1,12 @@ package com.velogm.domain.repository +import com.velogm.domain.OutResult import com.velogm.domain.model.PostList import com.velogm.domain.model.Tag import kotlinx.coroutines.flow.Flow interface TagRepository { - suspend fun getTag(): Flow> + suspend fun getTag(): Flow>> suspend fun getPopularTag():Flow> suspend fun deleteTag(tag:String):Flow suspend fun addTag(tag:String):Flow diff --git a/domain/src/main/java/com/velogm/domain/usecase/GetTagUseCase.kt b/domain/src/main/java/com/velogm/domain/usecase/GetTagUseCase.kt index bc95e0d..8e50a06 100644 --- a/domain/src/main/java/com/velogm/domain/usecase/GetTagUseCase.kt +++ b/domain/src/main/java/com/velogm/domain/usecase/GetTagUseCase.kt @@ -1,5 +1,6 @@ package com.velogm.domain.usecase +import com.velogm.domain.OutResult import com.velogm.domain.model.Tag import com.velogm.domain.repository.TagRepository import kotlinx.coroutines.flow.Flow @@ -7,6 +8,6 @@ import kotlinx.coroutines.flow.Flow class GetTagUseCase( private val repository: TagRepository ) { - suspend operator fun invoke(): Flow> = + suspend operator fun invoke(): Flow>> = repository.getTag() } \ No newline at end of file diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index 98926fb..e587dd7 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -26,6 +26,9 @@ android { buildConfigField("String", "CLIENT_ID", Properties().apply { load(project.rootProject.file("local.properties").inputStream()) }["client.id"].toString()) + buildConfigField("String", "VERSION_NAMES", Properties().apply { + load(project.rootProject.file("local.properties").inputStream()) + }["versions.name"].toString()) } release { isMinifyEnabled = false @@ -98,4 +101,6 @@ dependencies { implementation(Google.GOOGLE_FIREBASE_ANALYTICS) implementation(Google.GOOGLE_FIREBASE_MESSAGING) + implementation("com.google.firebase:firebase-config-ktx") + } \ No newline at end of file diff --git a/presentation/src/main/java/com/velogm/presentation/ui/MainActivity.kt b/presentation/src/main/java/com/velogm/presentation/ui/MainActivity.kt index 1a91ce7..6d581a2 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/MainActivity.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/MainActivity.kt @@ -1,5 +1,6 @@ package com.velogm.presentation.ui +import android.app.AlertDialog import android.content.Intent import android.os.Bundle import android.view.View @@ -9,6 +10,11 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import androidx.navigation.ui.setupWithNavController +import com.velogm.presentation.BuildConfig +import com.google.firebase.ktx.Firebase +import com.google.firebase.remoteconfig.FirebaseRemoteConfig +import com.google.firebase.remoteconfig.ktx.remoteConfig +import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.velogm.core_ui.base.BindingActivity import com.velogm.core_ui.context.longToast import com.velogm.core_ui.view.UiState @@ -16,20 +22,23 @@ import com.velogm.presentation.R import com.velogm.presentation.databinding.ActivityMainBinding import com.velogm.presentation.ui.signin.SignCheck import com.velogm.presentation.ui.signin.SignInActivity -import com.velogm.presentation.ui.signin.SignViewModel +import com.velogm.presentation.ui.signin.SignInViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import timber.log.Timber @AndroidEntryPoint class MainActivity : BindingActivity(R.layout.activity_main) { - private val mainViewModel by viewModels() - + private val mainViewModel by viewModels() + private lateinit var remoteConfig: FirebaseRemoteConfig override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() setupLogoutState() setUpWithdrawalState() + setRemoteConfig() + fetchAppVersion() } private fun initView() { @@ -94,4 +103,36 @@ class MainActivity : BindingActivity(R.layout.activity_main } } + + private fun setRemoteConfig() { + remoteConfig = Firebase.remoteConfig + val configSettings = remoteConfigSettings { + minimumFetchIntervalInSeconds = 1800 + } + remoteConfig.setConfigSettingsAsync(configSettings) + } + + private fun fetchAppVersion() { + var appVersion = remoteConfig.getString(REMOTE_KEY_APP_VERSION) + + remoteConfig.fetchAndActivate() + .addOnCompleteListener { + if (it.isSuccessful) { + if (appVersion.equals(BuildConfig.VERSION_NAMES)) + Timber.tag("remoteConfig").d("${BuildConfig.VERSION_NAMES}") + else { + AlertDialog.Builder(this) + .setTitle("Alert Version") + .setMessage("새로운 ${appVersion}이 출시했습니다.") + .show() + } + } else { + Timber.tag("remoteConfig").d("fail") + } + } + } + + companion object { + private const val REMOTE_KEY_APP_VERSION = "app_version" + } } \ No newline at end of file diff --git a/presentation/src/main/java/com/velogm/presentation/ui/SplashActivity.kt b/presentation/src/main/java/com/velogm/presentation/ui/SplashActivity.kt index baa99ff..fe3a134 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/SplashActivity.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/SplashActivity.kt @@ -6,14 +6,14 @@ import android.os.Bundle import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import com.velogm.presentation.ui.signin.SignInActivity -import com.velogm.presentation.ui.signin.SignViewModel +import com.velogm.presentation.ui.signin.SignInViewModel import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber @AndroidEntryPoint class SplashActivity : AppCompatActivity() { - private val viewModel by viewModels() + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (viewModel.getCheckLogin()) { diff --git a/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagFragment.kt b/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagFragment.kt index 5af9344..4cc76e9 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagFragment.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.KeyEvent import android.view.View import android.view.inputmethod.EditorInfo +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope @@ -17,10 +18,10 @@ import com.velogm.presentation.model.TagModel import com.velogm.presentation.ui.addtag.adapter.AddTagAdapter import com.velogm.presentation.ui.addtag.adapter.PopularTagAdapter import com.velogm.presentation.ui.addtag.dialog.DeleteDialogFragment +import com.velogm.presentation.ui.signin.SignInViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import timber.log.Timber @AndroidEntryPoint class AddTagFragment : BindingFragment(R.layout.fragment_add_tag) { @@ -28,27 +29,16 @@ class AddTagFragment : BindingFragment(R.layout.fragment_ private lateinit var myTagAdapter: AddTagAdapter private lateinit var popularTagAdapter: PopularTagAdapter private val viewModel by viewModels() + private val parentViewModel by activityViewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setNavigation() + setAdapter() collectMyTagListData() collectPopularTagListData() addTag() collectEventData() - myTagAdapter = AddTagAdapter(deleteTagClick = { - Timber.tag("deleteTag").d(it.tag) - val dialog = DeleteDialogFragment( - deleteTag = { - viewModel.deleteTag(it.tag) - } - ) - dialog.show(childFragmentManager, "delete") - }) - binding.rvAddTagList.adapter = myTagAdapter - popularTagAdapter = PopularTagAdapter(tagClick = { - viewModel.addTag(it.tag) - }) - binding.rvAddTagPopularList.adapter = popularTagAdapter //QA임시 코드 popularTagAdapter.submitList( listOf @@ -72,8 +62,25 @@ class AddTagFragment : BindingFragment(R.layout.fragment_ } } + private fun setAdapter() { + myTagAdapter = AddTagAdapter(deleteTagClick = { + val dialog = DeleteDialogFragment( + deleteTag = { + viewModel.deleteTag(it.tag) + } + ) + dialog.show(childFragmentManager, "delete") + }) + binding.rvAddTagList.adapter = myTagAdapter + popularTagAdapter = PopularTagAdapter(tagClick = { + viewModel.addTag(it.tag) + }) + binding.rvAddTagPopularList.adapter = popularTagAdapter + } + + private fun collectMyTagListData() { - viewModel.tagListData.flowWithLifecycle(lifecycle).onEach { + parentViewModel.tagListData.flowWithLifecycle(lifecycle).onEach { when (it) { is UiState.Success -> { myTagAdapter.submitList(it.data) @@ -87,9 +94,6 @@ class AddTagFragment : BindingFragment(R.layout.fragment_ private fun collectPopularTagListData() { viewModel.tagPopularListData.flowWithLifecycle(lifecycle).onEach { when (it) { - is UiState.Loading -> { - } - is UiState.Success -> { popularTagAdapter.submitList(it.data) } @@ -118,9 +122,8 @@ class AddTagFragment : BindingFragment(R.layout.fragment_ when (it) { is UiState.Success -> { toast("태그가 추가 되었습니다.") - viewModel.getTag() + parentViewModel.getTag() } - else -> {} } }.launchIn(lifecycleScope) diff --git a/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagViewModel.kt b/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagViewModel.kt index 3052a29..9e07fef 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagViewModel.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/addtag/AddTagViewModel.kt @@ -19,15 +19,11 @@ import javax.inject.Inject @HiltViewModel class AddTagViewModel @Inject constructor( - private val getTagUseCase: GetTagUseCase, private val getPopularTagUseCase: GetPopularTagUseCase, private val deleteTagUseCase: DeleteTagUseCase, private val addTagUseCase: AddTagUseCase ) : ViewModel() { - private val _tagListData = MutableStateFlow>>(UiState.Loading) - val tagListData: StateFlow>> = _tagListData.asStateFlow() - private val _tagPopularListData = MutableStateFlow>>(UiState.Loading) val tagPopularListData: StateFlow>> = _tagPopularListData.asStateFlow() @@ -37,15 +33,6 @@ class AddTagViewModel @Inject constructor( init { getPopularTag() - getTag() - } - - fun getTag() = viewModelScope.launch { - getTagUseCase().collect { - val tagList = it.toTagModelEntity() - _tagListData.value = UiState.Success(tagList) - Timber.d(it.toString()) - } } fun getPopularTag() = viewModelScope.launch { @@ -56,17 +43,17 @@ class AddTagViewModel @Inject constructor( } } - fun deleteTag(tag:String) = viewModelScope.launch { + fun deleteTag(tag: String) = viewModelScope.launch { deleteTagUseCase(tag).collect { - _eventData.value=UiState.Success(true) + _eventData.value = UiState.Success(true) } - _eventData.value=UiState.Loading + _eventData.value = UiState.Loading } - fun addTag(tag:String) = viewModelScope.launch { + fun addTag(tag: String) = viewModelScope.launch { addTagUseCase(tag).collect { - _eventData.value=UiState.Success(true) + _eventData.value = UiState.Success(true) } - _eventData.value=UiState.Loading + _eventData.value = UiState.Loading } } \ No newline at end of file diff --git a/presentation/src/main/java/com/velogm/presentation/ui/home/HomeFragment.kt b/presentation/src/main/java/com/velogm/presentation/ui/home/HomeFragment.kt index 1fd17bf..07a1fba 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/home/HomeFragment.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/home/HomeFragment.kt @@ -1,10 +1,9 @@ package com.velogm.presentation.ui.home -import android.annotation.SuppressLint import android.os.Bundle import android.view.View import androidx.core.os.bundleOf -import androidx.fragment.app.viewModels +import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController @@ -15,6 +14,7 @@ import com.velogm.core_ui.view.UiState import com.velogm.presentation.R import com.velogm.presentation.databinding.FragmentHomeBinding import com.velogm.presentation.model.TagModel +import com.velogm.presentation.ui.signin.SignInViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -25,29 +25,32 @@ class HomeFragment : BindingFragment(R.layout.fragment_home private lateinit var homeCollectionAdapter: HomeCollectionAdapter private lateinit var viewPager: ViewPager2 - private val viewModel by viewModels() + private val parentViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.getTag() + + navigateBack() + initAdapter() + collectTagListData() + moveToAddTag() + } + + private fun navigateBack() { binding.ivSearchBtn.setOnClickListener { findNavController().navigate( R.id.navigation_home_to_home_search, bundleOf( ) ) } - collectTagListData() - moveToAddTag() + } + + private fun initAdapter() { homeCollectionAdapter = HomeCollectionAdapter(childFragmentManager, lifecycle) viewPager = binding.pager - viewPager.offscreenPageLimit=6 + viewPager.offscreenPageLimit = 6 viewPager.adapter = homeCollectionAdapter - } - override fun onResume() { - super.onResume() - viewModel.getTag() - } private fun moveToAddTag() { binding.ivPlusBtn.setOnClickListener { @@ -59,7 +62,7 @@ class HomeFragment : BindingFragment(R.layout.fragment_home } private fun collectTagListData() { - viewModel.tagListData.flowWithLifecycle(lifecycle).onEach { + parentViewModel.tagListData.flowWithLifecycle(lifecycle).onEach { when (it) { is UiState.Success -> { initAdapter(it.data) diff --git a/presentation/src/main/java/com/velogm/presentation/ui/home/HomeViewModel.kt b/presentation/src/main/java/com/velogm/presentation/ui/home/HomeViewModel.kt deleted file mode 100644 index 22c15c3..0000000 --- a/presentation/src/main/java/com/velogm/presentation/ui/home/HomeViewModel.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.velogm.presentation.ui.home - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.velogm.core_ui.view.UiState -import com.velogm.domain.usecase.GetTagUseCase -import com.velogm.presentation.mapper.toTagModelEntity -import com.velogm.presentation.model.TagModel -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject - -@HiltViewModel -class HomeViewModel @Inject constructor( - private val getTagUseCase: GetTagUseCase -) : ViewModel() { - - private val _tagListData = MutableStateFlow>>(UiState.Loading) - val tagListData: StateFlow>> = _tagListData.asStateFlow() - fun getTag() = viewModelScope.launch { - getTagUseCase().collect { - val tagList = it.toTagModelEntity() - _tagListData.value = UiState.Success(tagList) - Timber.d(it.toString()) - } - } - -} \ No newline at end of file diff --git a/presentation/src/main/java/com/velogm/presentation/ui/home/screenhome/ScreenHomeSlidePageFragment.kt b/presentation/src/main/java/com/velogm/presentation/ui/home/screenhome/ScreenHomeSlidePageFragment.kt index 0c79cab..1c3b4e5 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/home/screenhome/ScreenHomeSlidePageFragment.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/home/screenhome/ScreenHomeSlidePageFragment.kt @@ -32,16 +32,7 @@ class ScreenHomeSlidePageFragment : val data = arguments?.getString("data") collectData(data) - postAdapter = PostAdapter(bookMarkClick = { - val intent = Intent(requireContext(), WebViewActivity::class.java).apply { - putExtra("url", it.url) - putExtra("followName", it.name) - putExtra("subscribed", it.subscribed) - } - startActivity(intent) - }) - binding.rvPostList.adapter = postAdapter - + initAdapter() collectPostListData(data) } @@ -53,6 +44,18 @@ class ScreenHomeSlidePageFragment : } } + private fun initAdapter() { + postAdapter = PostAdapter(bookMarkClick = { + val intent = Intent(requireContext(), WebViewActivity::class.java).apply { + putExtra("url", it.url) + putExtra("followName", it.name) + putExtra("subscribed", it.subscribed) + } + startActivity(intent) + }) + binding.rvPostList.adapter = postAdapter + } + private fun collectPostListData(data: String?) { viewModel.postListData.flowWithLifecycle(lifecycle).onEach { when (it) { diff --git a/presentation/src/main/java/com/velogm/presentation/ui/search/SearchFragment.kt b/presentation/src/main/java/com/velogm/presentation/ui/search/SearchFragment.kt index 1b6dc8e..154f305 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/search/SearchFragment.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/search/SearchFragment.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.core.widget.doAfterTextChanged +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope @@ -16,6 +17,8 @@ import com.velogm.presentation.databinding.FragmentHomeSearchBinding import com.velogm.presentation.model.TagModel import com.velogm.presentation.ui.addtag.adapter.PopularTagAdapter import com.velogm.presentation.ui.home.screenhome.adapter.PostAdapter +import com.velogm.presentation.ui.home.screenhome.adapter.PostTagAdapter +import com.velogm.presentation.ui.signin.SignInViewModel import com.velogm.presentation.ui.webview.WebViewActivity import com.velogm.presentation.util.Debouncer import dagger.hilt.android.AndroidEntryPoint @@ -32,6 +35,7 @@ class SearchFragment : BindingFragment(R.layout.fragm private lateinit var recentSearchWordAdapter: RecentSearchWordAdapter private val searchDebouncer = Debouncer() private val viewModel by viewModels() + private val parentViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -92,6 +96,18 @@ class SearchFragment : BindingFragment(R.layout.fragm } } + private fun collectMyTagListData() { + parentViewModel.tagListData.flowWithLifecycle(lifecycle).onEach { + when (it) { + is UiState.Success -> { + postTagAdapter.submitList(it.data) + } + + else -> {} + } + }.launchIn(lifecycleScope) + } + private fun collectPopularTagListData() { viewModel.tagPopularListData.flowWithLifecycle(lifecycle).onEach { when (it) { diff --git a/presentation/src/main/java/com/velogm/presentation/ui/search/SearchViewModel.kt b/presentation/src/main/java/com/velogm/presentation/ui/search/SearchViewModel.kt index 4c27a67..7ca64cc 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/search/SearchViewModel.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/search/SearchViewModel.kt @@ -36,8 +36,6 @@ class SearchViewModel @Inject constructor( private val deleteRecentSearchWordUseCase: DeleteRecentSearchWordUseCase ) : ViewModel() { - private val _tagListData = MutableStateFlow>>(UiState.Loading) - val tagListData: StateFlow>> = _tagListData.asStateFlow() private val _tagPopularListData = MutableStateFlow>>(UiState.Loading) val tagPopularListData: StateFlow>> = _tagPopularListData.asStateFlow() diff --git a/presentation/src/main/java/com/velogm/presentation/ui/setting/SettingFragment.kt b/presentation/src/main/java/com/velogm/presentation/ui/setting/SettingFragment.kt index a8b7155..03b31a5 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/setting/SettingFragment.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/setting/SettingFragment.kt @@ -6,13 +6,13 @@ import androidx.fragment.app.activityViewModels import com.velogm.core_ui.base.BindingFragment import com.velogm.presentation.R import com.velogm.presentation.databinding.FragmentSettingBinding -import com.velogm.presentation.ui.signin.SignViewModel +import com.velogm.presentation.ui.signin.SignInViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class SettingFragment : BindingFragment(R.layout.fragment_setting) { - private val viewModel by activityViewModels() + private val viewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/presentation/src/main/java/com/velogm/presentation/ui/signin/SignInActivity.kt b/presentation/src/main/java/com/velogm/presentation/ui/signin/SignInActivity.kt index ff5ca0b..66e48f1 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/signin/SignInActivity.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/signin/SignInActivity.kt @@ -28,7 +28,7 @@ class SignInActivity : AppCompatActivity() { private lateinit var binding: ActivitySignInBinding private lateinit var googleSignResultLauncher: ActivityResultLauncher - private val viewModel by viewModels() + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/presentation/src/main/java/com/velogm/presentation/ui/signin/SignViewModel.kt b/presentation/src/main/java/com/velogm/presentation/ui/signin/SignInViewModel.kt similarity index 59% rename from presentation/src/main/java/com/velogm/presentation/ui/signin/SignViewModel.kt rename to presentation/src/main/java/com/velogm/presentation/ui/signin/SignInViewModel.kt index 6bbe7c0..a7a9f41 100644 --- a/presentation/src/main/java/com/velogm/presentation/ui/signin/SignViewModel.kt +++ b/presentation/src/main/java/com/velogm/presentation/ui/signin/SignInViewModel.kt @@ -3,8 +3,13 @@ package com.velogm.presentation.ui.signin import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.velogm.core_ui.view.UiState +import com.velogm.domain.NetworkErrorHandling +import com.velogm.domain.collectOutResult import com.velogm.domain.repository.AuthRepository import com.velogm.domain.usecase.AccessTokenUseCase +import com.velogm.domain.usecase.GetTagUseCase +import com.velogm.presentation.mapper.toTagModelEntity +import com.velogm.presentation.model.TagModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -14,11 +19,15 @@ import timber.log.Timber import javax.inject.Inject @HiltViewModel -class SignViewModel @Inject constructor( +class SignInViewModel @Inject constructor( + private val getTagUseCase: GetTagUseCase, private val tokenUseCase: AccessTokenUseCase, private val authRepository: AuthRepository ) : ViewModel() { + private val _tagListData = MutableStateFlow>>(UiState.Loading) + val tagListData: StateFlow>> = _tagListData.asStateFlow() + private val _token = MutableStateFlow>(UiState.Loading) val token: StateFlow> = _token.asStateFlow() @@ -28,6 +37,31 @@ class SignViewModel @Inject constructor( private val _withdrawal = MutableStateFlow>(UiState.Loading) val withdrawal: StateFlow> = _withdrawal.asStateFlow() + init { + getTag() + } + + fun getTag() = viewModelScope.launch { + getTagUseCase().collectOutResult( + handleSuccess = { + val tagList = it.data.toTagModelEntity() + _tagListData.value = UiState.Success(tagList) + Timber.d(it.toString()) + }, + handleFail = { + val errorHandling = when (it.error?.message) { + "401" -> NetworkErrorHandling.Unauthorized + "500" -> NetworkErrorHandling.ServerError + else -> NetworkErrorHandling.OtherError + } + when (errorHandling) { + is NetworkErrorHandling.Unauthorized -> postLogout() + is NetworkErrorHandling.ServerError -> Timber.d("서버에러") + is NetworkErrorHandling.OtherError -> Timber.d("다른에러") + } + } + ) + } fun getGoogleLogin(code: String) = viewModelScope.launch { tokenUseCase(code).collect { token -> _token.value = UiState.Success(token.accessToken)