From cce2687312003258f96a3a8a321b48329f00dc7e Mon Sep 17 00:00:00 2001 From: null Date: Mon, 6 Jul 2026 22:12:47 -0500 Subject: [PATCH] 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 --- .../java/app/closer/ui/dates/BucketListViewModel.kt | 11 ++++++----- .../java/app/closer/ui/dates/DateBuilderViewModel.kt | 7 ++++--- .../app/closer/ui/messages/ConversationViewModel.kt | 5 +++-- .../app/closer/ui/messages/MessagesInboxViewModel.kt | 7 ++++--- .../closer/ui/questions/QuestionThreadViewModel.kt | 4 ++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/app/closer/ui/dates/BucketListViewModel.kt b/app/src/main/java/app/closer/ui/dates/BucketListViewModel.kt index c356178a..c4e489db 100644 --- a/app/src/main/java/app/closer/ui/dates/BucketListViewModel.kt +++ b/app/src/main/java/app/closer/ui/dates/BucketListViewModel.kt @@ -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 { diff --git a/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt b/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt index 86d43da7..f8c386be 100644 --- a/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt +++ b/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt @@ -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 { diff --git a/app/src/main/java/app/closer/ui/messages/ConversationViewModel.kt b/app/src/main/java/app/closer/ui/messages/ConversationViewModel.kt index 8c053a3a..c0629006 100644 --- a/app/src/main/java/app/closer/ui/messages/ConversationViewModel.kt +++ b/app/src/main/java/app/closer/ui/messages/ConversationViewModel.kt @@ -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 = _uiState.asStateFlow() diff --git a/app/src/main/java/app/closer/ui/messages/MessagesInboxViewModel.kt b/app/src/main/java/app/closer/ui/messages/MessagesInboxViewModel.kt index 3239e98f..4c91e20e 100644 --- a/app/src/main/java/app/closer/ui/messages/MessagesInboxViewModel.kt +++ b/app/src/main/java/app/closer/ui/messages/MessagesInboxViewModel.kt @@ -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 = _uiState.asStateFlow() diff --git a/app/src/main/java/app/closer/ui/questions/QuestionThreadViewModel.kt b/app/src/main/java/app/closer/ui/questions/QuestionThreadViewModel.kt index be9882cd..300db718 100644 --- a/app/src/main/java/app/closer/ui/questions/QuestionThreadViewModel.kt +++ b/app/src/main/java/app/closer/ui/questions/QuestionThreadViewModel.kt @@ -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