Simplify database handling.
Some checks are pending
continuous-integration/drone/push Build is running
Some checks are pending
continuous-integration/drone/push Build is running
This commit is contained in:
@@ -3,6 +3,8 @@ package me.zobrist.tichucounter.data
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import me.zobrist.tichucounter.data.entity.Game
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
|
||||
@Database(entities = [Round::class, Game::class], version = 1)
|
||||
@TypeConverters(DateConverter::class)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.util.*
|
||||
|
||||
@Entity
|
||||
data class Game(
|
||||
override var active: Boolean,
|
||||
override var nameA: String,
|
||||
override var nameB: String,
|
||||
override val created: Date,
|
||||
override var modified: Date,
|
||||
@PrimaryKey(autoGenerate = true) override val uid: Long? = null
|
||||
) : IGame, IEntity
|
||||
@@ -1,16 +0,0 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
import androidx.room.Entity
|
||||
import java.util.*
|
||||
|
||||
@Entity
|
||||
data class GameAndScore(
|
||||
override var active: Boolean,
|
||||
override var nameA: String,
|
||||
override var nameB: String,
|
||||
override val created: Date,
|
||||
override var modified: Date,
|
||||
override var gameId: Long,
|
||||
override var scoreA: Int,
|
||||
override var scoreB: Int,
|
||||
) : IGame, IRound
|
||||
@@ -2,6 +2,7 @@ package me.zobrist.tichucounter.data
|
||||
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.zobrist.tichucounter.data.entity.Game
|
||||
|
||||
|
||||
@Dao
|
||||
@@ -10,19 +11,13 @@ interface GameDao : DaoBase<Game> {
|
||||
@Query("SELECT * FROM game")
|
||||
fun getAll(): Flow<List<Game>>
|
||||
|
||||
@Query(
|
||||
"SELECT active, " +
|
||||
"nameA, " +
|
||||
"nameB, " +
|
||||
"created, " +
|
||||
"modified, " +
|
||||
"game.uid as gameId, " +
|
||||
"COALESCE(SUM(round.scoreA), 0) as scoreA, " +
|
||||
"COALESCE(SUM(round.scoreB), 0) as scoreB " +
|
||||
"FROM game " +
|
||||
"LEFT JOIN round ON round.gameId = game.uid GROUP BY game.uid ORDER BY modified DESC"
|
||||
)
|
||||
fun getAllWithPoints(): Flow<List<GameAndScore>>
|
||||
@Transaction
|
||||
@Query("SELECT * FROM game where uid ")
|
||||
fun getGamesWithRounds(): Flow<List<GameWithScores>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM game WHERE active is 1")
|
||||
fun getActiveWithRounds(): Flow<GameWithScores?>
|
||||
|
||||
@Query("SELECT * FROM game WHERE uid is :gameId")
|
||||
fun getGameById(gameId: Long): Flow<Game>
|
||||
@@ -30,6 +25,7 @@ interface GameDao : DaoBase<Game> {
|
||||
@Query("SELECT * FROM game WHERE active is 1")
|
||||
fun getActive(): Flow<Game?>
|
||||
|
||||
|
||||
@Query("UPDATE game SET active = 1 WHERE uid is :gameId;")
|
||||
fun setActive(gameId: Long)
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Relation
|
||||
import me.zobrist.tichucounter.data.entity.Game
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
|
||||
@Entity
|
||||
data class GameWithScores(
|
||||
@Embedded val game: Game,
|
||||
@Relation(
|
||||
parentColumn = "uid",
|
||||
entityColumn = "gameId"
|
||||
)
|
||||
val rounds: List<Round>
|
||||
)
|
||||
@@ -1,5 +0,0 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
interface IEntity {
|
||||
val uid: Long?
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
import java.util.*
|
||||
|
||||
interface IGame {
|
||||
var active: Boolean
|
||||
var nameA: String
|
||||
var nameB: String
|
||||
val created: Date
|
||||
var modified: Date
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
interface IRound {
|
||||
var gameId: Long
|
||||
var scoreA: Int
|
||||
var scoreB: Int
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity
|
||||
data class Round(
|
||||
override var gameId: Long,
|
||||
override var scoreA: Int,
|
||||
override var scoreB: Int,
|
||||
@PrimaryKey(autoGenerate = true) override val uid: Long? = null
|
||||
) : IRound, IEntity
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.zobrist.tichucounter.data
|
||||
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
|
||||
@Dao
|
||||
interface RoundDao : DaoBase<Round> {
|
||||
@@ -12,20 +12,4 @@ interface RoundDao : DaoBase<Round> {
|
||||
@Query("SELECT * FROM round WHERE gameId is :gameId")
|
||||
fun getAllForGame(gameId: Long?): List<Round>
|
||||
|
||||
@Query(
|
||||
"SELECT gameId, SUM(scoreA) as scoreA, SUM(scoreB) as scoreB " +
|
||||
"FROM round " +
|
||||
"LEFT JOIN game ON game.uid = round.gameId " +
|
||||
"WHERE game.active == 1"
|
||||
)
|
||||
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
|
||||
fun getRoundSumForActiveGame(): Flow<Round>
|
||||
|
||||
@Query(
|
||||
"SELECT gameId, scoreA, scoreB, round.uid " +
|
||||
"FROM round " +
|
||||
"LEFT JOIN game ON game.uid = round.gameId " +
|
||||
"WHERE game.active == 1"
|
||||
)
|
||||
fun getForActiveGame(): Flow<List<Round>>
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.zobrist.tichucounter.data.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import me.zobrist.tichucounter.data.entity.IEntity
|
||||
import java.util.*
|
||||
|
||||
@Entity
|
||||
data class Game(
|
||||
var active: Boolean,
|
||||
var nameA: String,
|
||||
var nameB: String,
|
||||
val created: Date,
|
||||
var modified: Date,
|
||||
@PrimaryKey(autoGenerate = true) override val uid: Long = 0
|
||||
) : IEntity
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.zobrist.tichucounter.data.entity
|
||||
|
||||
interface IEntity {
|
||||
val uid: Long
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package me.zobrist.tichucounter.data.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import me.zobrist.tichucounter.data.entity.IEntity
|
||||
|
||||
@Entity
|
||||
data class Round(
|
||||
var gameId: Long,
|
||||
var scoreA: Int,
|
||||
var scoreB: Int,
|
||||
@PrimaryKey(autoGenerate = true) override val uid: Long = 0
|
||||
) : IEntity
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.zobrist.tichucounter.domain
|
||||
|
||||
import me.zobrist.tichucounter.data.GameWithScores
|
||||
|
||||
class GameWithScoresExtension {
|
||||
}
|
||||
|
||||
fun GameWithScores.getTotalPoints(): Pair<Int, Int> {
|
||||
var scoreA = 0
|
||||
var scoreB = 0
|
||||
|
||||
this.rounds.forEach {
|
||||
scoreA += it.scoreA
|
||||
scoreB += it.scoreB
|
||||
}
|
||||
return Pair(scoreA, scoreB)
|
||||
}
|
||||
@@ -2,13 +2,13 @@ package me.zobrist.tichucounter.repository
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.zobrist.tichucounter.data.Game
|
||||
import me.zobrist.tichucounter.data.GameDao
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
import me.zobrist.tichucounter.data.RoundDao
|
||||
import me.zobrist.tichucounter.data.*
|
||||
import me.zobrist.tichucounter.data.entity.Game
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -116,4 +116,12 @@ class GameRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getActiveGameFlow(): Flow<GameWithScores?> {
|
||||
return gameDao.getActiveWithRounds()
|
||||
}
|
||||
|
||||
fun getAllWithRoundFlow(): Flow<List<GameWithScores>> {
|
||||
return gameDao.getGamesWithRounds()
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
import me.zobrist.tichucounter.data.RoundDao
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
import me.zobrist.tichucounter.domain.NavigationAction
|
||||
import me.zobrist.tichucounter.domain.TopBarAction
|
||||
import me.zobrist.tichucounter.repository.GameRepository
|
||||
@@ -19,8 +18,7 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class MainViewModel @Inject constructor(
|
||||
private val gameRepository: GameRepository,
|
||||
roundDao: RoundDao
|
||||
private val gameRepository: GameRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private var redoRounds = mutableStateListOf<Round>()
|
||||
@@ -37,14 +35,18 @@ class MainViewModel @Inject constructor(
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
roundDao.getForActiveGame().collect {
|
||||
isUndoActionActive = it.isNotEmpty()
|
||||
|
||||
if (expectedRoundCount != it.count()) {
|
||||
redoRounds.clear()
|
||||
gameRepository.getActiveGameFlow().collect {
|
||||
|
||||
if (it != null) {
|
||||
isUndoActionActive = it.rounds.isNotEmpty()
|
||||
|
||||
if (expectedRoundCount != it.rounds.count()) {
|
||||
redoRounds.clear()
|
||||
}
|
||||
|
||||
expectedRoundCount = it.rounds.count()
|
||||
}
|
||||
|
||||
expectedRoundCount = it.count()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
import me.zobrist.tichucounter.ui.AppTheme
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,9 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.GameDao
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
import me.zobrist.tichucounter.data.RoundDao
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
import me.zobrist.tichucounter.domain.Tichu
|
||||
import me.zobrist.tichucounter.domain.getTotalPoints
|
||||
import me.zobrist.tichucounter.repository.GameRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -48,9 +47,7 @@ interface ICounterViewModel {
|
||||
|
||||
@HiltViewModel
|
||||
class CounterViewModel @Inject constructor(
|
||||
private val gameRepository: GameRepository,
|
||||
private val roundDao: RoundDao,
|
||||
private val gameDao: GameDao
|
||||
private val gameRepository: GameRepository
|
||||
) :
|
||||
ViewModel(), ICounterViewModel {
|
||||
|
||||
@@ -112,24 +109,18 @@ class CounterViewModel @Inject constructor(
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
roundDao.getForActiveGame().collect {
|
||||
roundScoreList = it
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
gameDao.getActive().collect {
|
||||
gameRepository.getActiveGameFlow().collect {
|
||||
if (it != null) {
|
||||
teamNameA = it.nameA
|
||||
teamNameB = it.nameB
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
roundDao.getRoundSumForActiveGame().collect { score ->
|
||||
totalScoreA = score.scoreA
|
||||
totalScoreB = score.scoreB
|
||||
val score = it.getTotalPoints()
|
||||
|
||||
roundScoreList = it.rounds
|
||||
totalScoreA = score.first
|
||||
totalScoreB = score.second
|
||||
|
||||
teamNameA = it.game.nameA
|
||||
teamNameB = it.game.nameB
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
import me.zobrist.tichucounter.ui.AppTheme
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -15,7 +15,10 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.zobrist.tichucounter.R
|
||||
import me.zobrist.tichucounter.data.GameAndScore
|
||||
import me.zobrist.tichucounter.data.entity.Game
|
||||
import me.zobrist.tichucounter.data.GameWithScores
|
||||
import me.zobrist.tichucounter.data.entity.Round
|
||||
import me.zobrist.tichucounter.domain.getTotalPoints
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
@@ -65,7 +68,7 @@ fun DeleteConfirmDialog(show: Boolean = true, onExecuted: (Boolean) -> Unit = {}
|
||||
|
||||
@Composable
|
||||
fun HistoryList(
|
||||
games: List<GameAndScore>,
|
||||
games: List<GameWithScores>,
|
||||
onOpenClicked: (GameId: Long) -> Unit,
|
||||
onDeleteClicked: (GameId: Long) -> Unit
|
||||
) {
|
||||
@@ -80,7 +83,7 @@ fun HistoryList(
|
||||
|
||||
@Composable
|
||||
fun HistoryListItem(
|
||||
game: GameAndScore,
|
||||
game: GameWithScores,
|
||||
onOpenClicked: (GameId: Long) -> Unit,
|
||||
onDeleteClicked: (GameId: Long) -> Unit
|
||||
) {
|
||||
@@ -88,13 +91,15 @@ fun HistoryListItem(
|
||||
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
|
||||
|
||||
|
||||
val cardColor = if (game.active) {
|
||||
val cardColor = if (game.game.active) {
|
||||
CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer)
|
||||
|
||||
} else {
|
||||
CardDefaults.cardColors()
|
||||
}
|
||||
|
||||
val totalScores = game.getTotalPoints()
|
||||
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -107,18 +112,18 @@ fun HistoryListItem(
|
||||
) {
|
||||
Column(Modifier.weight(4f)) {
|
||||
Text(
|
||||
text = game.nameA + " vs " + game.nameB,
|
||||
text = game.game.nameA + " vs " + game.game.nameB,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Text(
|
||||
text = game.scoreA.toString() + " : " + game.scoreB.toString(),
|
||||
text = totalScores.first.toString() + " : " + totalScores.second.toString(),
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(5.dp))
|
||||
Text(
|
||||
text = format.format(game.modified),
|
||||
text = format.format(game.game.modified),
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
}
|
||||
@@ -128,11 +133,11 @@ fun HistoryListItem(
|
||||
.width(70.dp)
|
||||
) {
|
||||
|
||||
ElevatedButton(onClick = { onOpenClicked(game.gameId) }, enabled = true) {
|
||||
ElevatedButton(onClick = { onOpenClicked(game.game.uid) }, enabled = true) {
|
||||
Icon(Icons.Outlined.OpenInFull, null)
|
||||
}
|
||||
ElevatedButton(
|
||||
onClick = { onDeleteClicked(game.gameId) }, enabled = !game.active
|
||||
onClick = { onDeleteClicked(game.game.uid) }, enabled = !game.game.active
|
||||
) {
|
||||
Icon(Icons.Outlined.Delete, null)
|
||||
}
|
||||
@@ -145,11 +150,26 @@ fun HistoryListItem(
|
||||
@Composable
|
||||
private fun HistoryListPreview() {
|
||||
val tempData = listOf(
|
||||
GameAndScore(true, "abc", "def", Date(), Date(), 1, 10, 50),
|
||||
GameAndScore(false, "ADTH", "dogfg", Date(), Date(), 2, 20, 60),
|
||||
GameAndScore(false, "TeamA3 langer Name", "TeamB3", Date(), Date(), 3, 30, 70),
|
||||
GameAndScore(false, "TeamA4", "TeamB4", Date(), Date(), 4, 40, 80),
|
||||
GameAndScore(false, "TeamA5", "TeamB5", Date(), Date(), 5, 50, 90)
|
||||
GameWithScores(
|
||||
Game(true, "abc", "def", Date(), Date()),
|
||||
listOf(Round(1, 550, 500))
|
||||
),
|
||||
GameWithScores(
|
||||
Game(false, "ADTH", "dogfg", Date(), Date()),
|
||||
listOf(Round(2, 20, 60))
|
||||
),
|
||||
GameWithScores(
|
||||
Game(false, "TeamA3 langer Name", "TeamB3", Date(), Date()),
|
||||
listOf(Round(3, 30, 70))
|
||||
),
|
||||
GameWithScores(
|
||||
Game(false, "TeamA4", "TeamB4", Date(), Date()),
|
||||
listOf(Round(4, 40, 80))
|
||||
),
|
||||
GameWithScores(
|
||||
Game(false, "TeamA5", "TeamB5", Date(), Date()),
|
||||
listOf(Round(5, 50, 90))
|
||||
)
|
||||
)
|
||||
HistoryList(tempData, {}) {}
|
||||
}
|
||||
|
||||
@@ -7,24 +7,23 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.GameAndScore
|
||||
import me.zobrist.tichucounter.data.GameDao
|
||||
import me.zobrist.tichucounter.data.GameWithScores
|
||||
import me.zobrist.tichucounter.repository.GameRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class HistoryViewModel @Inject constructor(
|
||||
private val gameDao: GameDao,
|
||||
private val gameRepository: GameRepository
|
||||
) : ViewModel() {
|
||||
|
||||
var gameAndHistory by mutableStateOf(emptyList<GameAndScore>())
|
||||
var gameAndHistory by mutableStateOf(emptyList<GameWithScores>())
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
gameDao.getAllWithPoints().collect { games ->
|
||||
|
||||
gameRepository.getAllWithRoundFlow().collect() { games ->
|
||||
gameAndHistory = games
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user