refactor: extract startGameWithCouple, remove duplicate partner notification from leaveCouple
This commit is contained in:
parent
2521e91db4
commit
fb0e8fbaab
|
|
@ -2,7 +2,6 @@ package app.closer.data.remote
|
||||||
|
|
||||||
import app.closer.domain.model.Couple
|
import app.closer.domain.model.Couple
|
||||||
import com.google.firebase.firestore.DocumentSnapshot
|
import com.google.firebase.firestore.DocumentSnapshot
|
||||||
import com.google.firebase.firestore.FieldValue
|
|
||||||
import com.google.firebase.firestore.FirebaseFirestore
|
import com.google.firebase.firestore.FirebaseFirestore
|
||||||
import com.google.firebase.firestore.SetOptions
|
import com.google.firebase.firestore.SetOptions
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
@ -112,31 +111,8 @@ class FirestoreCoupleDataSource @Inject constructor(private val db: FirebaseFire
|
||||||
.addOnFailureListener { cont.resumeWithException(it) }
|
.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")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun DocumentSnapshot.toCouple() = Couple(
|
private fun DocumentSnapshot.toCouple() = Couple(
|
||||||
id = id,
|
id = id,
|
||||||
|
|
|
||||||
|
|
@ -51,32 +51,12 @@ class GameSessionManager @Inject constructor(
|
||||||
): Result<String> {
|
): Result<String> {
|
||||||
val couple = coupleRepository.getCoupleForUser(userId)
|
val couple = coupleRepository.getCoupleForUser(userId)
|
||||||
?: return Result.failure(Exception("User is not in a couple"))
|
?: return Result.failure(Exception("User is not in a couple"))
|
||||||
|
return startGameWithCouple(userId, couple, gameType, categoryId, questionIds)
|
||||||
val activeSession = sessionRepository.getActiveSessionForCouple(couple.id)
|
.map { it.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 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* Returns a [GameHandle] on success. Fails if the user is solo or a session
|
||||||
* is already active (the failure message carries partner/game context).
|
* 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"))
|
?: return Result.failure(Exception("Not signed in"))
|
||||||
val couple = coupleRepository.getCoupleForUser(userId)
|
val couple = coupleRepository.getCoupleForUser(userId)
|
||||||
?: return Result.failure(Exception("User is not in a couple"))
|
?: return Result.failure(Exception("User is not in a couple"))
|
||||||
return startGame(userId, gameType, categoryId, questionIds)
|
return startGameWithCouple(userId, couple, gameType, categoryId, questionIds)
|
||||||
.map { sessionId -> GameHandle(sessionId, couple.id) }
|
.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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue