fix: ViewModel and repository error handling improvements
This commit is contained in:
parent
c89e4f6bba
commit
e5c734d3b2
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.data.repository
|
package app.closer.data.repository
|
||||||
|
|
||||||
|
import app.closer.core.crash.CrashReporter
|
||||||
import app.closer.data.remote.FirestoreCoupleDataSource
|
import app.closer.data.remote.FirestoreCoupleDataSource
|
||||||
import app.closer.data.remote.FirestoreUserDataSource
|
import app.closer.data.remote.FirestoreUserDataSource
|
||||||
import app.closer.domain.model.Couple
|
import app.closer.domain.model.Couple
|
||||||
|
|
@ -10,12 +11,17 @@ import javax.inject.Singleton
|
||||||
@Singleton
|
@Singleton
|
||||||
class CoupleRepositoryImpl @Inject constructor(
|
class CoupleRepositoryImpl @Inject constructor(
|
||||||
private val coupleDataSource: FirestoreCoupleDataSource,
|
private val coupleDataSource: FirestoreCoupleDataSource,
|
||||||
private val userDataSource: FirestoreUserDataSource
|
private val userDataSource: FirestoreUserDataSource,
|
||||||
|
private val crashReporter: CrashReporter
|
||||||
) : CoupleRepository {
|
) : CoupleRepository {
|
||||||
|
|
||||||
override suspend fun getCoupleForUser(userId: String): Couple? {
|
override suspend fun getCoupleForUser(userId: String): Couple? {
|
||||||
val coupleId = runCatching { userDataSource.getUser(userId)?.coupleId }.getOrNull() ?: return null
|
val coupleId = runCatching { userDataSource.getUser(userId)?.coupleId }
|
||||||
return runCatching { coupleDataSource.getCoupleById(coupleId) }.getOrNull()
|
.onFailure { crashReporter.recordException(it) }
|
||||||
|
.getOrNull() ?: return null
|
||||||
|
return runCatching { coupleDataSource.getCoupleById(coupleId) }
|
||||||
|
.onFailure { crashReporter.recordException(it) }
|
||||||
|
.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun createCouple(inviterUserId: String, acceptorUserId: String, inviteCode: String): Result<String> = runCatching {
|
override suspend fun createCouple(inviterUserId: String, acceptorUserId: String, inviteCode: String): Result<String> = runCatching {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.data.repository
|
package app.closer.data.repository
|
||||||
|
|
||||||
|
import app.closer.core.crash.CrashReporter
|
||||||
import app.closer.data.remote.FirestoreCollections
|
import app.closer.data.remote.FirestoreCollections
|
||||||
import app.closer.domain.model.QuestionSession
|
import app.closer.domain.model.QuestionSession
|
||||||
import app.closer.domain.repository.QuestionSessionRepository
|
import app.closer.domain.repository.QuestionSessionRepository
|
||||||
|
|
@ -10,7 +11,8 @@ import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class QuestionSessionRepositoryImpl @Inject constructor(
|
class QuestionSessionRepositoryImpl @Inject constructor(
|
||||||
private val firestore: FirebaseFirestore
|
private val firestore: FirebaseFirestore,
|
||||||
|
private val crashReporter: CrashReporter
|
||||||
) : QuestionSessionRepository {
|
) : QuestionSessionRepository {
|
||||||
|
|
||||||
override suspend fun saveSession(session: QuestionSession): Result<Unit> = runCatching {
|
override suspend fun saveSession(session: QuestionSession): Result<Unit> = runCatching {
|
||||||
|
|
@ -64,7 +66,9 @@ class QuestionSessionRepositoryImpl @Inject constructor(
|
||||||
isPremium = doc.getBoolean("isPremium") ?: false,
|
isPremium = doc.getBoolean("isPremium") ?: false,
|
||||||
status = doc.getString("status") ?: "completed"
|
status = doc.getString("status") ?: "completed"
|
||||||
)
|
)
|
||||||
}.getOrNull()
|
}
|
||||||
|
.onFailure { crashReporter.recordException(it) }
|
||||||
|
.getOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.ui.dates
|
package app.closer.ui.dates
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.closer.domain.model.DateIdea
|
import app.closer.domain.model.DateIdea
|
||||||
|
|
@ -62,7 +63,9 @@ class DateMatchViewModel @Inject constructor(
|
||||||
val uid = authRepository.currentUserId
|
val uid = authRepository.currentUserId
|
||||||
val couple = uid?.let { runCatching { coupleRepository.getCoupleForUser(it) }.getOrNull() }
|
val couple = uid?.let { runCatching { coupleRepository.getCoupleForUser(it) }.getOrNull() }
|
||||||
val partnerName = couple?.userIds?.firstOrNull { it != uid }?.let { partnerId ->
|
val partnerName = couple?.userIds?.firstOrNull { it != uid }?.let { partnerId ->
|
||||||
runCatching { userRepository.getUser(partnerId)?.displayName }.getOrNull()
|
runCatching { userRepository.getUser(partnerId)?.displayName }
|
||||||
|
.onFailure { Log.w(TAG, "Could not load partner display name", it) }
|
||||||
|
.getOrNull()
|
||||||
}
|
}
|
||||||
val ideas = repository.getDateIdeas()
|
val ideas = repository.getDateIdeas()
|
||||||
_uiState.value = DateMatchUiState(
|
_uiState.value = DateMatchUiState(
|
||||||
|
|
@ -145,4 +148,8 @@ class DateMatchViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun retry() = loadDateMatch()
|
fun retry() = loadDateMatch()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "DateMatchViewModel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.ui.dates
|
package app.closer.ui.dates
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.closer.data.repository.DateIdeaSeed
|
import app.closer.data.repository.DateIdeaSeed
|
||||||
|
|
@ -59,7 +60,9 @@ class DateMatchesViewModel @Inject constructor(
|
||||||
val couple = uid?.let { runCatching { coupleRepository.getCoupleForUser(it) }.getOrNull() }
|
val couple = uid?.let { runCatching { coupleRepository.getCoupleForUser(it) }.getOrNull() }
|
||||||
val partnerId = couple?.userIds?.firstOrNull { it != uid }
|
val partnerId = couple?.userIds?.firstOrNull { it != uid }
|
||||||
val partnerName = partnerId?.let { id ->
|
val partnerName = partnerId?.let { id ->
|
||||||
runCatching { userRepository.getUser(id)?.displayName }.getOrNull()
|
runCatching { userRepository.getUser(id)?.displayName }
|
||||||
|
.onFailure { Log.w(TAG, "Could not load partner display name", it) }
|
||||||
|
.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
_uiState.value = DateMatchesUiState(
|
_uiState.value = DateMatchesUiState(
|
||||||
|
|
@ -132,4 +135,8 @@ class DateMatchesViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun retry() = loadMatches()
|
fun retry() = loadMatches()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "DateMatchesViewModel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.ui.home
|
package app.closer.ui.home
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.closer.domain.model.LocalAnswer
|
import app.closer.domain.model.LocalAnswer
|
||||||
|
|
@ -106,7 +107,9 @@ class HomeViewModel @Inject constructor(
|
||||||
val uid = authRepository.currentUserId
|
val uid = authRepository.currentUserId
|
||||||
val couple = uid?.let { runCatching { coupleRepository.getCoupleForUser(it) }.getOrNull() }
|
val couple = uid?.let { runCatching { coupleRepository.getCoupleForUser(it) }.getOrNull() }
|
||||||
val partnerName = couple?.userIds?.firstOrNull { it != uid }?.let { partnerId ->
|
val partnerName = couple?.userIds?.firstOrNull { it != uid }?.let { partnerId ->
|
||||||
runCatching { userRepository.getUser(partnerId)?.displayName }.getOrNull()
|
runCatching { userRepository.getUser(partnerId)?.displayName }
|
||||||
|
.onFailure { Log.w(TAG, "Could not load partner display name", it) }
|
||||||
|
.getOrNull()
|
||||||
}
|
}
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
|
@ -261,4 +264,8 @@ class HomeViewModel @Inject constructor(
|
||||||
split("_", "-")
|
split("_", "-")
|
||||||
.filter { part -> part.isNotBlank() }
|
.filter { part -> part.isNotBlank() }
|
||||||
.joinToString(" ") { part -> part.replaceFirstChar { it.uppercaseChar() } }
|
.joinToString(" ") { part -> part.replaceFirstChar { it.uppercaseChar() } }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "HomeViewModel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.ui.onboarding
|
package app.closer.ui.onboarding
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.closer.domain.model.AuthState
|
import app.closer.domain.model.AuthState
|
||||||
|
|
@ -34,7 +35,9 @@ class OnboardingViewModel @Inject constructor(
|
||||||
is AuthState.Loading -> _uiState.update { it.copy(isCheckingAuth = true) }
|
is AuthState.Loading -> _uiState.update { it.copy(isCheckingAuth = true) }
|
||||||
is AuthState.Unauthenticated -> _uiState.update { it.copy(isCheckingAuth = false, navigateTo = null) }
|
is AuthState.Unauthenticated -> _uiState.update { it.copy(isCheckingAuth = false, navigateTo = null) }
|
||||||
is AuthState.Authenticated -> {
|
is AuthState.Authenticated -> {
|
||||||
val user = runCatching { userRepository.getUser(authState.userId) }.getOrNull()
|
val user = runCatching { userRepository.getUser(authState.userId) }
|
||||||
|
.onFailure { Log.w(TAG, "Could not load user profile during onboarding", it) }
|
||||||
|
.getOrNull()
|
||||||
val destination = when {
|
val destination = when {
|
||||||
user == null || user.displayName.isBlank() -> "create_profile"
|
user == null || user.displayName.isBlank() -> "create_profile"
|
||||||
else -> "home"
|
else -> "home"
|
||||||
|
|
@ -47,4 +50,8 @@ class OnboardingViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }
|
fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "OnboardingViewModel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.ui.pairing
|
package app.closer.ui.pairing
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
|
@ -46,7 +47,9 @@ class InviteConfirmViewModel @Inject constructor(
|
||||||
.onSuccess { invite ->
|
.onSuccess { invite ->
|
||||||
loadedInvite = invite
|
loadedInvite = invite
|
||||||
val inviterName = invite?.let {
|
val inviterName = invite?.let {
|
||||||
runCatching { userRepository.getUser(it.inviterUserId)?.displayName }.getOrNull()
|
runCatching { userRepository.getUser(it.inviterUserId)?.displayName }
|
||||||
|
.onFailure { e -> Log.w(TAG, "Could not load inviter display name", e) }
|
||||||
|
.getOrNull()
|
||||||
}
|
}
|
||||||
_uiState.update { it.copy(isLoading = false, inviterName = inviterName ?: "your partner") }
|
_uiState.update { it.copy(isLoading = false, inviterName = inviterName ?: "your partner") }
|
||||||
}
|
}
|
||||||
|
|
@ -80,4 +83,8 @@ class InviteConfirmViewModel @Inject constructor(
|
||||||
|
|
||||||
fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }
|
fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }
|
||||||
fun dismissError() = _uiState.update { it.copy(error = null) }
|
fun dismissError() = _uiState.update { it.copy(error = null) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "InviteConfirmViewModel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.ui.settings
|
package app.closer.ui.settings
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.closer.core.navigation.AppRoute
|
import app.closer.core.navigation.AppRoute
|
||||||
|
|
@ -45,11 +46,15 @@ class SettingsViewModel @Inject constructor(
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val email = authRepository.currentUserEmail ?: ""
|
val email = authRepository.currentUserEmail ?: ""
|
||||||
val user = runCatching { userRepository.getUser(userId) }.getOrNull()
|
val user = runCatching { userRepository.getUser(userId) }
|
||||||
|
.onFailure { Log.w(TAG, "Could not load current user profile", it) }
|
||||||
|
.getOrNull()
|
||||||
val couple = coupleRepository.getCoupleForUser(userId)
|
val couple = coupleRepository.getCoupleForUser(userId)
|
||||||
val partnerId = couple?.userIds?.firstOrNull { it != userId }
|
val partnerId = couple?.userIds?.firstOrNull { it != userId }
|
||||||
val partnerName = partnerId?.let {
|
val partnerName = partnerId?.let {
|
||||||
runCatching { userRepository.getUser(it)?.displayName }.getOrNull()
|
runCatching { userRepository.getUser(it)?.displayName }
|
||||||
|
.onFailure { e -> Log.w(TAG, "Could not load partner display name", e) }
|
||||||
|
.getOrNull()
|
||||||
}
|
}
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
|
@ -72,4 +77,8 @@ class SettingsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }
|
fun onNavigated() = _uiState.update { it.copy(navigateTo = null) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SettingsViewModel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package app.closer.ui.wheel
|
package app.closer.ui.wheel
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
|
@ -40,7 +41,9 @@ class SpinWheelViewModel @Inject constructor(
|
||||||
|
|
||||||
private fun loadCategory() {
|
private fun loadCategory() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val category = runCatching { repository.getCategoryById(categoryId) }.getOrNull()
|
val category = runCatching { repository.getCategoryById(categoryId) }
|
||||||
|
.onFailure { Log.w(TAG, "Could not load wheel category", it) }
|
||||||
|
.getOrNull()
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
|
|
@ -56,7 +59,9 @@ class SpinWheelViewModel @Inject constructor(
|
||||||
_uiState.update { it.copy(isSpinning = true, error = null) }
|
_uiState.update { it.copy(isSpinning = true, error = null) }
|
||||||
val questions = runCatching {
|
val questions = runCatching {
|
||||||
repository.getQuestionsByCategory(categoryId).shuffled().take(SESSION_SIZE)
|
repository.getQuestionsByCategory(categoryId).shuffled().take(SESSION_SIZE)
|
||||||
}.getOrElse { emptyList() }
|
}
|
||||||
|
.onFailure { Log.w(TAG, "Could not load wheel questions", it) }
|
||||||
|
.getOrElse { emptyList() }
|
||||||
|
|
||||||
if (questions.isEmpty()) {
|
if (questions.isEmpty()) {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
|
|
@ -65,7 +70,9 @@ class SpinWheelViewModel @Inject constructor(
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
val category = runCatching { repository.getCategoryById(categoryId) }.getOrNull()
|
val category = runCatching { repository.getCategoryById(categoryId) }
|
||||||
|
.onFailure { Log.w(TAG, "Could not load wheel category for session", it) }
|
||||||
|
.getOrNull()
|
||||||
sessionStore.activeSession = LocalWheelSession(
|
sessionStore.activeSession = LocalWheelSession(
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
categoryName = category?.displayName ?: categoryId,
|
categoryName = category?.displayName ?: categoryId,
|
||||||
|
|
@ -85,5 +92,6 @@ class SpinWheelViewModel @Inject constructor(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SESSION_SIZE = 10
|
const val SESSION_SIZE = 10
|
||||||
|
private const val TAG = "SpinWheelViewModel"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue