157 lines
5.4 KiB
Kotlin
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"
|
|
}
|
|
}
|