feat(chess): finished games section below active games in lobby

Adds a "Finished Games" section to the Android chess lobby screen,
showing up to 10 completed games with opponent avatars, result
(Won/Lost/Draw), and move count. The section only appears when
completedGames is non-empty, matching the Desktop implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
davotoula
2026-03-23 16:24:28 +01:00
parent 012e97494e
commit ed9d942ce0
@@ -67,6 +67,7 @@ import com.vitorpamplona.amethyst.commons.chess.ActiveGameCard
import com.vitorpamplona.amethyst.commons.chess.ChallengeCard
import com.vitorpamplona.amethyst.commons.chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.chess.ChessSyncBanner
import com.vitorpamplona.amethyst.commons.chess.CompletedGameCard
import com.vitorpamplona.amethyst.commons.chess.NewChessGameDialog
import com.vitorpamplona.amethyst.commons.chess.OutgoingChallengeCard
import com.vitorpamplona.amethyst.commons.chess.OverlappingAvatars
@@ -295,13 +296,15 @@ fun ChessLobbyContent(
val spectatingGames by chessViewModel.spectatingGames.collectAsState()
val publicGames by chessViewModel.publicGames.collectAsState()
val challenges by chessViewModel.challenges.collectAsState()
val completedGames by chessViewModel.completedGames.collectAsState()
val userPubkey = accountViewModel.account.userProfile().pubkeyHex
val hasContent =
activeGames.isNotEmpty() ||
spectatingGames.isNotEmpty() ||
publicGames.isNotEmpty() ||
challenges.isNotEmpty()
challenges.isNotEmpty() ||
completedGames.isNotEmpty()
if (!hasContent) {
// Empty state - use LazyColumn so pull-to-refresh works
@@ -560,6 +563,54 @@ fun ChessLobbyContent(
}
}
// Finished games section
if (completedGames.isNotEmpty()) {
item {
Spacer(Modifier.height(16.dp))
Text(
"Finished Games",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(vertical = 8.dp),
)
}
items(
completedGames.distinctBy { it.gameId }.take(10),
key = { "completed-${it.gameId}-${it.completedAt}" },
) { game ->
val opponentPubkey =
if (game.whitePubkey == userPubkey) game.blackPubkey else game.whitePubkey
val opponentUser =
remember(opponentPubkey) {
accountViewModel.checkGetOrCreateUser(opponentPubkey)
}
val opponentName =
opponentUser?.toBestDisplayName()
?: game.blackDisplayName
?: game.whiteDisplayName
?: opponentPubkey.take(8)
CompletedGameCard(
opponentName = opponentName,
result = game.result,
didUserWin = game.didUserWin(userPubkey),
isDraw = game.isDraw,
moveCount = game.moveCount,
avatar = {
OverlappingAvatars(
avatar1Hex = userPubkey,
avatar2Hex = opponentPubkey,
avatar1Url =
remember(userPubkey) {
accountViewModel.checkGetOrCreateUser(userPubkey)
}?.profilePicture(),
avatar2Url = opponentUser?.profilePicture(),
)
},
)
}
}
// Bottom padding for FAB
item {
Spacer(Modifier.height(80.dp))