Closer/app/src/main/java/app/closer/ui/wheel/SpinWheelViewModel.kt

157 lines
5.4 KiB
Kotlin

package app.closer.ui.wheel
import android.util.Log
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.GameType
import app.closer.domain.model.SessionLength
import app.closer.domain.repository.QuestionRepository
import app.closer.domain.usecase.GameSessionManager
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
data class SpinWheelUiState(
val isLoading: Boolean = true,
val categoryName: String = "",
val isSpinning: Boolean = false,
val spunAndReady: Boolean = false,
val selectedLength: SessionLength = SessionLength.STANDARD,
val error: String? = null,
val navigateTo: String? = null
)
@HiltViewModel
class SpinWheelViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val repository: QuestionRepository,
private val sessionStore: LocalWheelSessionStore,
private val gameSessionManager: GameSessionManager
) : ViewModel() {
private val categoryId: String = savedStateHandle["categoryId"] ?: ""
private val _uiState = MutableStateFlow(SpinWheelUiState())
val uiState: StateFlow<SpinWheelUiState> = _uiState.asStateFlow()
init {
loadCategory()
}
private fun loadCategory() {
viewModelScope.launch {
val category = runCatching { repository.getCategoryById(categoryId) }
.onFailure { Log.w(TAG, "Could not load wheel category", it) }
.getOrNull()
_uiState.update {
it.copy(
isLoading = false,
categoryName = category?.displayName ?: categoryId
)
}
}
}
fun spin() {
if (_uiState.value.isSpinning) return
viewModelScope.launch {
_uiState.update { it.copy(isSpinning = true, error = null) }
val questions = runCatching {
repository.getQuestionsByCategory(categoryId).shuffled().take(_uiState.value.selectedLength.count)
}
.onFailure { Log.w(TAG, "Could not load wheel questions", it) }
.getOrElse { emptyList() }
if (questions.isEmpty()) {
_uiState.update {
it.copy(isSpinning = false, error = "No questions found for this category.")
}
return@launch
}
val category = runCatching { repository.getCategoryById(categoryId) }
.onFailure { Log.w(TAG, "Could not load wheel category for session", it) }
.getOrNull()
sessionStore.activeSession = LocalWheelSession(
categoryId = categoryId,
categoryName = category?.displayName ?: categoryId,
questions = questions
)
_uiState.update { it.copy(isSpinning = false, spunAndReady = true) }
}
}
fun startSession() {
viewModelScope.launch {
val userId = gameSessionManager.currentUserId
if (userId == null) {
_uiState.update { it.copy(error = "Not logged in") }
return@launch
}
val couple = runCatching {
gameSessionManager.getCoupleForUser(userId)
}.getOrNull()
if (couple == null) {
_uiState.update { it.copy(error = "Not in a couple") }
return@launch
}
val hasActive = runCatching {
gameSessionManager.hasActiveSession(couple.id)
}.getOrNull() ?: false
if (hasActive) {
_uiState.update {
it.copy(navigateTo = AppRoute.WAITING_FOR_PARTNER)
}
return@launch
}
val questionIds = sessionStore.activeSession?.questions?.map { it.id }
val startResult = runCatching {
gameSessionManager.startGame(
userId = userId,
gameType = GameType.WHEEL,
categoryId = categoryId,
questionIds = questionIds
)
}.getOrElse { Result.failure(it) }
when {
startResult.isSuccess -> {
val sessionId = startResult.getOrThrow()
_uiState.update { it.copy(navigateTo = AppRoute.wheelSession(sessionId)) }
}
startResult.exceptionOrNull()?.message?.startsWith("partner_active_session|") == true -> {
_uiState.update { it.copy(navigateTo = AppRoute.WAITING_FOR_PARTNER) }
}
else -> {
Log.w(TAG, "Could not start wheel session", startResult.exceptionOrNull())
_uiState.update { it.copy(error = "Could not start session") }
}
}
}
}
fun setLength(len: SessionLength) {
// Resets spunAndReady so the user re-spins with the new count.
_uiState.update { it.copy(selectedLength = len, spunAndReady = false) }
}
fun onNavigated() {
_uiState.update { it.copy(navigateTo = null) }
}
companion object {
private const val TAG = "SpinWheelViewModel"
}
}