refactor: extract startGameWithCouple, remove duplicate partner notification from leaveCouple

This commit is contained in:
null 2026-06-18 03:08:48 -05:00
parent 2521e91db4
commit fb0e8fbaab
2 changed files with 33 additions and 49 deletions

View File

@ -2,7 +2,6 @@ package app.closer.data.remote
import app.closer.domain.model.Couple
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.SetOptions
import kotlinx.coroutines.suspendCancellableCoroutine
@ -112,31 +111,8 @@ class FirestoreCoupleDataSource @Inject constructor(private val db: FirebaseFire
.addOnFailureListener { cont.resumeWithException(it) }
}
// After successfully clearing the couple state, queue an in-app notification
// for the partner so they see the unpair event even if FCM is delayed.
if (!partnerId.isNullOrBlank()) {
writePartnerLeftNotification(partnerId)
}
}
private suspend fun writePartnerLeftNotification(partnerId: String): Unit =
suspendCancellableCoroutine { cont ->
db.collection(FirestoreCollections.USERS)
.document(partnerId)
.collection("notification_queue")
.add(
mapOf(
"type" to "partner_left",
"title" to "Your partner has left",
"body" to "You are no longer paired.",
"read" to false,
"createdAt" to FieldValue.serverTimestamp()
)
)
.addOnSuccessListener { cont.resume(Unit) }
.addOnFailureListener { cont.resumeWithException(it) }
}
@Suppress("UNCHECKED_CAST")
private fun DocumentSnapshot.toCouple() = Couple(
id = id,

View File

@ -51,32 +51,12 @@ class GameSessionManager @Inject constructor(
): Result<String> {
val couple = coupleRepository.getCoupleForUser(userId)
?: return Result.failure(Exception("User is not in a couple"))
val activeSession = sessionRepository.getActiveSessionForCouple(couple.id)
if (activeSession != null) {
val partnerId = couple.userIds.firstOrNull { it != userId }
val partnerName = partnerId?.let { userRepository.getUser(it) }?.displayName ?: "Partner"
val gameTypeLabel = gameTypeLabel(gameType)
return Result.failure(
Exception("partner_active_session|$partnerName|$gameTypeLabel")
)
}
val session = QuestionSession(
coupleId = couple.id,
categoryId = categoryId ?: "",
questionIds = questionIds ?: emptyList(),
startedByUserId = userId,
gameType = gameType,
status = "active"
)
val saveResult = sessionRepository.saveSession(session)
return saveResult.map { session.id }
return startGameWithCouple(userId, couple, gameType, categoryId, questionIds)
.map { it.id }
}
/**
* Start a game for the currently signed-in user, resolving their couple.
* Start a game for the currently signed-in user, resolving their couple once.
* Returns a [GameHandle] on success. Fails if the user is solo or a session
* is already active (the failure message carries partner/game context).
*/
@ -89,8 +69,36 @@ class GameSessionManager @Inject constructor(
?: return Result.failure(Exception("Not signed in"))
val couple = coupleRepository.getCoupleForUser(userId)
?: return Result.failure(Exception("User is not in a couple"))
return startGame(userId, gameType, categoryId, questionIds)
.map { sessionId -> GameHandle(sessionId, couple.id) }
return startGameWithCouple(userId, couple, gameType, categoryId, questionIds)
.map { session -> GameHandle(session.id, couple.id) }
}
private suspend fun startGameWithCouple(
userId: String,
couple: Couple,
gameType: String,
categoryId: String?,
questionIds: List<String>?
): Result<QuestionSession> {
val activeSession = sessionRepository.getActiveSessionForCouple(couple.id)
if (activeSession != null) {
val partnerId = couple.userIds.firstOrNull { it != userId }
val partnerName = partnerId?.let { userRepository.getUser(it) }?.displayName ?: "Partner"
return Result.failure(
Exception("partner_active_session|$partnerName|${gameTypeLabel(gameType)}")
)
}
val session = QuestionSession(
coupleId = couple.id,
categoryId = categoryId ?: "",
questionIds = questionIds ?: emptyList(),
startedByUserId = userId,
gameType = gameType,
status = "active"
)
return sessionRepository.saveSession(session).map { session }
}
/**