refactor(ui): inject AuthRepository instead of FirebaseAuth.getInstance()

Five ViewModels reached into the FirebaseAuth.getInstance() static singleton for
the current user id; switch them to the injected AuthRepository.currentUserId
(BucketList, QuestionThread, DateBuilder, MessagesInbox, Conversation). Removes
the Firebase-in-presentation-layer coupling and makes them testable. No behavior
change; compiles + unit suite green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-06 22:12:47 -05:00
parent 89cbd7cb55
commit cce2687312
5 changed files with 19 additions and 15 deletions

View File

@ -4,9 +4,9 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.domain.model.BucketListItem
import app.closer.domain.repository.AuthRepository
import app.closer.domain.repository.BucketListRepository
import app.closer.domain.repository.CoupleRepository
import com.google.firebase.auth.FirebaseAuth
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@ -18,7 +18,8 @@ import kotlinx.coroutines.launch
@HiltViewModel
class BucketListViewModel @Inject constructor(
private val repository: BucketListRepository,
private val coupleRepository: CoupleRepository
private val coupleRepository: CoupleRepository,
private val authRepository: AuthRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(BucketListUiState())
@ -33,7 +34,7 @@ class BucketListViewModel @Inject constructor(
private fun resolveCouple() {
viewModelScope.launch {
val uid = FirebaseAuth.getInstance().currentUser?.uid ?: return@launch
val uid = authRepository.currentUserId ?: return@launch
runCatching { coupleRepository.getCoupleForUser(uid) }
.getOrNull()
?.let { setCoupleId(it.id) }
@ -69,7 +70,7 @@ class BucketListViewModel @Inject constructor(
title = title.take(MAX_TITLE_LENGTH),
description = description.take(MAX_DESCRIPTION_LENGTH),
category = category,
addedBy = FirebaseAuth.getInstance().currentUser?.uid ?: "",
addedBy = authRepository.currentUserId ?: "",
addedAt = System.currentTimeMillis()
)
@ -98,7 +99,7 @@ class BucketListViewModel @Inject constructor(
)
}
} else {
repository.completeItem(coupleId, itemId, FirebaseAuth.getInstance().currentUser?.uid ?: "")
repository.completeItem(coupleId, itemId, authRepository.currentUserId ?: "")
_uiState.update {
it.copy(
items = it.items.map {

View File

@ -5,9 +5,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.domain.model.DatePlan
import app.closer.domain.model.DatePlanStatus
import app.closer.domain.repository.AuthRepository
import app.closer.domain.repository.CoupleRepository
import app.closer.domain.repository.DatePlanRepository
import com.google.firebase.auth.FirebaseAuth
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@ -19,7 +19,8 @@ import kotlinx.coroutines.launch
@HiltViewModel
class DateBuilderViewModel @Inject constructor(
private val repository: DatePlanRepository,
private val coupleRepository: CoupleRepository
private val coupleRepository: CoupleRepository,
private val authRepository: AuthRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(DateBuilderUiState())
@ -50,7 +51,7 @@ class DateBuilderViewModel @Inject constructor(
// N-002: create a real PLANNED date_plan (surfaced on Home as the upcoming date) and resolve
// the couple ourselves. Previously this wrote a date_plan_preference keyed off a dateIdeaId
// that no entry ever set + an empty coupleId, so "Create Plan" silently saved nothing.
val uid = FirebaseAuth.getInstance().currentUser?.uid
val uid = authRepository.currentUserId
val couple = uid?.let { runCatching { coupleRepository.getCoupleForUser(it) }.getOrNull() }
if (couple == null) {
_uiState.update {

View File

@ -8,12 +8,12 @@ import app.closer.core.billing.CouplePremiumChecker
import app.closer.data.local.QuestionDao
import app.closer.data.local.mapper.toQuestion
import app.closer.domain.model.QuestionMessage
import app.closer.domain.repository.AuthRepository
import app.closer.domain.repository.ConversationRepository
import app.closer.domain.repository.CoupleRepository
import app.closer.domain.repository.UserRepository
import app.closer.notifications.ActiveThreadMonitor
import app.closer.ui.components.TextInputLimits
import com.google.firebase.auth.FirebaseAuth
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@ -60,12 +60,13 @@ class ConversationViewModel @Inject constructor(
private val activeThreadMonitor: ActiveThreadMonitor,
private val couplePremiumChecker: CouplePremiumChecker,
private val analyticsTracker: AnalyticsTracker,
authRepository: AuthRepository,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val coupleId: String = savedStateHandle["coupleId"] ?: ""
private val conversationId: String = savedStateHandle["conversationId"] ?: ""
val currentUserId: String = FirebaseAuth.getInstance().currentUser?.uid ?: ""
val currentUserId: String = authRepository.currentUserId ?: ""
private val _uiState = MutableStateFlow(ConversationUiState())
val uiState: StateFlow<ConversationUiState> = _uiState.asStateFlow()

View File

@ -5,10 +5,10 @@ import androidx.lifecycle.viewModelScope
import app.closer.data.local.QuestionDao
import app.closer.data.local.mapper.toQuestion
import app.closer.domain.model.Conversation
import app.closer.domain.repository.AuthRepository
import app.closer.domain.repository.ConversationRepository
import app.closer.domain.repository.CoupleRepository
import app.closer.domain.repository.UserRepository
import com.google.firebase.auth.FirebaseAuth
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -30,10 +30,11 @@ class MessagesInboxViewModel @Inject constructor(
private val repository: ConversationRepository,
private val coupleRepository: CoupleRepository,
private val userRepository: UserRepository,
private val questionDao: QuestionDao
private val questionDao: QuestionDao,
authRepository: AuthRepository
) : ViewModel() {
private val currentUserId: String = FirebaseAuth.getInstance().currentUser?.uid ?: ""
private val currentUserId: String = authRepository.currentUserId ?: ""
private val _uiState = MutableStateFlow(MessagesInboxUiState())
val uiState: StateFlow<MessagesInboxUiState> = _uiState.asStateFlow()

View File

@ -13,7 +13,6 @@ import app.closer.domain.model.QuestionMessage
import app.closer.domain.model.QuestionReaction
import app.closer.domain.repository.QuestionThreadRepository
import app.closer.ui.components.TextInputLimits
import com.google.firebase.auth.FirebaseAuth
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -54,12 +53,13 @@ class QuestionThreadViewModel @Inject constructor(
private val localAnswerRepository: app.closer.domain.repository.LocalAnswerRepository,
private val userRepository: app.closer.domain.repository.UserRepository,
private val coupleRepository: app.closer.domain.repository.CoupleRepository,
authRepository: app.closer.domain.repository.AuthRepository,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val coupleId: String = savedStateHandle["coupleId"] ?: ""
private val questionId: String = savedStateHandle["questionId"] ?: ""
val currentUserId: String = FirebaseAuth.getInstance().currentUser?.uid ?: ""
val currentUserId: String = authRepository.currentUserId ?: ""
// Released-once guard for our thread reveal key.
private var threadKeyReleased = false