From 36c2042ff159004e00faed2068f981b81164a502 Mon Sep 17 00:00:00 2001 From: juhwankim-dev Date: Fri, 26 Dec 2025 00:46:11 +0900 Subject: [PATCH 1/3] =?UTF-8?q?NR-122=20=ED=83=80=EC=9D=B4=EB=A8=B8,=20?= =?UTF-8?q?=ED=9E=8C=ED=8A=B8=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B4=80=EB=A6=AC=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B0=A9=EB=B2=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 왜? 힌트 화면에서 진행률만 확인하고 힌트 차감없이 되돌아가는 기능이 새로 개발될 예정임 기존에는 힌트를 차감하고 다음 화면으로 넘어가는 방식이였으나 앞으로는 힌트 화면에서 데이터의 변경을 담당하게 될거임. 근데 이걸 setFragmentResult로 하면 다소 코드가 너저분해지고 나중에 기능이 확장될 것을 생각하면 이 방식은 누가 누구에게 무엇을 넘겼는지 파악하기 어려워짐 어떻게? argument를 넘기는 방식에서 graph viewModel을 사용해 관리하는 방법으로 변경 --- .../presentation/ui/hint/HintFragment.kt | 20 +++++++++++ .../presentation/ui/hint/HintViewModel.kt | 28 ++++++---------- .../ui/main/GameSharedViewModel.kt | 33 +++++++++++++++++++ .../presentation/ui/main/TimerEvent.kt | 4 +-- .../presentation/ui/main/TimerFragment.kt | 5 ++- .../presentation/ui/main/TimerViewModel.kt | 3 +- .../main/res/navigation/game_navigation.xml | 15 +-------- 7 files changed, 70 insertions(+), 38 deletions(-) create mode 100644 presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintFragment.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintFragment.kt index cdf313a6..526915d1 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintFragment.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintFragment.kt @@ -5,6 +5,7 @@ import android.view.View import androidx.core.os.bundleOf import androidx.core.view.isVisible import androidx.fragment.app.viewModels +import androidx.hilt.navigation.fragment.hiltNavGraphViewModels import androidx.navigation.fragment.findNavController import androidx.viewpager2.widget.ViewPager2 import com.google.firebase.analytics.FirebaseAnalytics @@ -20,8 +21,10 @@ import com.nextroom.nextroom.presentation.extension.safeNavigate import com.nextroom.nextroom.presentation.extension.snackbar import com.nextroom.nextroom.presentation.extension.toTimerFormat import com.nextroom.nextroom.presentation.extension.updateSystemPadding +import com.nextroom.nextroom.presentation.ui.main.GameSharedViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.orbitmvi.orbit.viewmodel.observe import timber.log.Timber @@ -29,6 +32,7 @@ import timber.log.Timber class HintFragment : BaseFragment(FragmentHintBinding::inflate) { private val viewModel: HintViewModel by viewModels() + private val gameSharedViewModel: GameSharedViewModel by hiltNavGraphViewModels(R.id.game_navigation) private val state: HintState get() = viewModel.container.stateFlow.value @@ -44,6 +48,7 @@ class HintFragment : BaseFragment(FragmentHintBinding::infl super.onViewCreated(view, savedInstanceState) FirebaseAnalytics.getInstance(requireContext()).logEvent("screen_view", bundleOf("screen_name" to "hint")) initViews() + initSubscribe() viewModel.observe(viewLifecycleOwner, state = ::render, sideEffect = ::handleEvent) } @@ -70,6 +75,21 @@ class HintFragment : BaseFragment(FragmentHintBinding::infl } } + private fun initSubscribe() { + viewLifecycleOwner.repeatOnStarted { + launch { + gameSharedViewModel.currentHint.collect { hint -> + hint?.let { viewModel.setHint(it) } + } + } + launch { + gameSharedViewModel.subscribeStatus.collect { subscribeStatus -> + viewModel.setSubscribeStatus(subscribeStatus) + } + } + } + } + private fun render(state: HintState) = with(binding) { pbLoading.isVisible = state.loading tbHint.tvTitle.text = state.lastSeconds.toTimerFormat() diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintViewModel.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintViewModel.kt index 8912962c..2ecfb195 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintViewModel.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintViewModel.kt @@ -1,10 +1,8 @@ package com.nextroom.nextroom.presentation.ui.hint -import androidx.lifecycle.SavedStateHandle import com.mangbaam.commonutil.DateTimeUtil -import com.nextroom.nextroom.domain.repository.AdminRepository +import com.nextroom.nextroom.domain.model.SubscribeStatus import com.nextroom.nextroom.domain.repository.DataStoreRepository -import com.nextroom.nextroom.domain.repository.StatisticsRepository import com.nextroom.nextroom.domain.repository.TimerRepository import com.nextroom.nextroom.presentation.base.BaseViewModel import com.nextroom.nextroom.presentation.model.Hint @@ -18,20 +16,12 @@ import javax.inject.Inject @HiltViewModel class HintViewModel @Inject constructor( - savedStateHandle: SavedStateHandle, private val timerRepository: TimerRepository, - private val statsRepository: StatisticsRepository, - private val adminRepository: AdminRepository, private val dataStoreRepository: DataStoreRepository, ) : BaseViewModel() { override val container: Container = - container( - HintState( - hint = savedStateHandle.get("hint") ?: Hint(), - userSubscribeStatus = HintFragmentArgs.fromSavedStateHandle(savedStateHandle).subscribeStatus, - ) - ) + container(HintState()) private val state: HintState get() = container.stateFlow.value @@ -50,20 +40,22 @@ class HintViewModel @Inject constructor( updateNetworkDisconnectedCount(it) } } - /*viewModelScope.launch { - // 힌트 오픈 시간 통계 집계 - statsRepository.recordHintStats(HintStats(state.hint.id, DateTimeUtil().currentTime() ?: "", "")) - }*/ } private fun updateNetworkDisconnectedCount(count: Int) = intent { reduce { state.copy(networkDisconnectedCount = count) } } + fun setHint(hint: Hint) = intent { + reduce { state.copy(hint = hint) } + } + + fun setSubscribeStatus(subscribeStatus: SubscribeStatus) = intent { + reduce { state.copy(userSubscribeStatus = subscribeStatus) } + } + fun openAnswer() = intent { reduce { state.copy(hint = state.hint.copy(answerOpened = true)) } - // 정답 오픈 시간 통계 집계 -// statsRepository.recordAnswerOpenTime(state.hint.id, dateTimeUtil.currentTime() ?: "") } private fun tick(lastSeconds: Int) = intent { diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt new file mode 100644 index 00000000..db3a6b7c --- /dev/null +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt @@ -0,0 +1,33 @@ +package com.nextroom.nextroom.presentation.ui.main + +import androidx.lifecycle.SavedStateHandle +import com.nextroom.nextroom.domain.model.SubscribeStatus +import com.nextroom.nextroom.presentation.base.NewBaseViewModel +import com.nextroom.nextroom.presentation.model.Hint +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class GameSharedViewModel @Inject constructor( + savedStateHandle: SavedStateHandle +) : NewBaseViewModel() { + + private val _subscribeStatus = MutableStateFlow( + TimerFragmentArgs.fromSavedStateHandle(savedStateHandle).subscribeStatus + ) + val subscribeStatus: StateFlow = _subscribeStatus.asStateFlow() + + private val _currentHint = MutableStateFlow(null) + val currentHint: StateFlow = _currentHint.asStateFlow() + + fun setCurrentHint(hint: Hint) { + _currentHint.value = hint + } + + fun updateCurrentHint(hint: Hint) { + _currentHint.value = hint + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerEvent.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerEvent.kt index 9a6d776f..47d8e382 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerEvent.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerEvent.kt @@ -1,12 +1,10 @@ package com.nextroom.nextroom.presentation.ui.main -import com.nextroom.nextroom.domain.model.SubscribeStatus import com.nextroom.nextroom.presentation.model.Hint sealed interface TimerEvent { data class OnOpenHint( - val hint: Hint, - val subscribeStatus: SubscribeStatus + val hint: Hint ) : TimerEvent data object TimerFinish : TimerEvent diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerFragment.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerFragment.kt index 4be8a308..1f047e2c 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerFragment.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerFragment.kt @@ -12,6 +12,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.viewModels +import androidx.hilt.navigation.fragment.hiltNavGraphViewModels import androidx.navigation.fragment.findNavController import com.bumptech.glide.Glide import com.bumptech.glide.load.DataSource @@ -50,6 +51,7 @@ class TimerFragment : BaseFragment(FragmentTimerBinding::i private val viewModel: TimerViewModel by viewModels() private val painterViewModel: PainterViewModel by activityViewModels() + private val gameSharedViewModel: GameSharedViewModel by hiltNavGraphViewModels(R.id.game_navigation) private var gameStartConfirmDialog: NRDialog? = null @@ -266,7 +268,8 @@ class TimerFragment : BaseFragment(FragmentTimerBinding::i when (event) { is TimerEvent.ClearHintCode -> binding.customCodeInput.setCode("") is TimerEvent.OnOpenHint -> { - val action = TimerFragmentDirections.moveToHintFragment(event.hint, event.subscribeStatus) + gameSharedViewModel.setCurrentHint(event.hint) + val action = TimerFragmentDirections.moveToHintFragment() findNavController().safeNavigate(action) viewModel.clearHintCode() } diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerViewModel.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerViewModel.kt index 21dc38c0..de3e5907 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerViewModel.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/TimerViewModel.kt @@ -208,8 +208,7 @@ class TimerViewModel @Inject constructor( answerOpened = state.answerOpenedHints.contains(hint.id), hintImageUrlList = hint.hintImageUrlList.toList(), answerImageUrlList = hint.answerImageUrlList.toList() - ), - TimerFragmentArgs.fromSavedStateHandle(savedStateHandle).subscribeStatus + ) ), ) setGameState() diff --git a/presentation/src/main/res/navigation/game_navigation.xml b/presentation/src/main/res/navigation/game_navigation.xml index 995be402..46fcd5c3 100644 --- a/presentation/src/main/res/navigation/game_navigation.xml +++ b/presentation/src/main/res/navigation/game_navigation.xml @@ -12,14 +12,7 @@ - - - + app:destination="@id/hint_fragment" /> @@ -43,12 +36,6 @@ - - Date: Fri, 26 Dec 2025 00:50:20 +0900 Subject: [PATCH 2/3] =?UTF-8?q?NR-122=20HintState=EC=97=90=EC=84=9C=20Hint?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EC=8B=9C=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=B4=88=EA=B8=B0=EA=B0=92=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 왜? 굉장히 긴 샘플 문자열 데이터가 들어가있었는데 이렇게 되면 ui에서 실제 데이터가 로드되기 전 초기값을 보여주게 되었을때 사용자에게 노출될 가능성이 있음 (데이터 로드전에는 로딩으로 표시한다거나.. 처리를 하면 되긴하지만 놓칠 가능성이 있으니까) 그래서 불필요한 초기값 세팅들을 수정함 id도 -1로 하면 버그가 생길 가능성이 있어서 0으로 수정 --- .../nextroom/nextroom/presentation/model/Hint.kt | 2 +- .../nextroom/presentation/ui/hint/HintState.kt | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/model/Hint.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/model/Hint.kt index c777d55b..7a1de7d3 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/model/Hint.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/model/Hint.kt @@ -3,7 +3,7 @@ package com.nextroom.nextroom.presentation.model import java.io.Serializable data class Hint( - val id: Int = -1, + val id: Int = 0, val progress: Int = 0, val hint: String = "", val answer: String = "", diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintState.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintState.kt index 3e0b0646..816d943c 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintState.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/hint/HintState.kt @@ -1,25 +1,11 @@ package com.nextroom.nextroom.presentation.ui.hint import com.nextroom.nextroom.domain.model.SubscribeStatus -import com.nextroom.nextroom.domain.model.UserSubscribeStatus import com.nextroom.nextroom.presentation.model.Hint data class HintState( val loading: Boolean = false, - val hint: Hint = Hint( - id = 0, - progress = 0, - hint = "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. \n" + - "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 \n" + - "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 \n" + - "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. \n" + - "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. \n" + - "침대 옆 공간을 보시면 숫자와 카드 조침대 옆 공간을 보시면 숫자와 카드 조", - answer = "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. \n" + - "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 \n" + - "침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. 침대 옆 공간을 보시면 숫자와 카드 조합이 몇 가지 나와있는데 그걸 해답일지의 풀이법에 대입하면 정답이 나옵니다. 액자에 적힌 정보도 잘 체크해 보세요. ", - answerOpened = false, - ), + val hint: Hint = Hint(), val lastSeconds: Int = 0, val userSubscribeStatus: SubscribeStatus = SubscribeStatus.Default, val networkDisconnectedCount: Int = 0 From 02b08e2810fa47b4271f26f98aae53727e3a9736 Mon Sep 17 00:00:00 2001 From: juhwankim-dev Date: Fri, 26 Dec 2025 00:57:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?NR-122=20updateCurrentHint=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 왜? setCurrentHint와 역할이 겹쳐 제거한다. --- .../nextroom/presentation/ui/main/GameSharedViewModel.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt index db3a6b7c..d7ba213b 100644 --- a/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt +++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/ui/main/GameSharedViewModel.kt @@ -26,8 +26,4 @@ class GameSharedViewModel @Inject constructor( fun setCurrentHint(hint: Hint) { _currentHint.value = hint } - - fun updateCurrentHint(hint: Hint) { - _currentHint.value = hint - } } \ No newline at end of file