diff --git a/build.gradle.kts b/build.gradle.kts index 56a945b..ac0ac44 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,5 +6,7 @@ plugins { id("org.jetbrains.kotlin.android") apply false id("org.jetbrains.compose") apply false id("androidx.room") version "2.6.1" apply false + id("app.cash.sqldelight") version "2.0.1" apply false id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22" apply false + kotlin("kapt") version "1.9.22" apply false } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 8402328..e37364b 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -4,7 +4,9 @@ plugins { id("com.android.library") id("kotlin-parcelize") id("androidx.room") + id("app.cash.sqldelight") id("org.jetbrains.kotlin.plugin.serialization") + kotlin("kapt") } kotlin { @@ -25,6 +27,8 @@ kotlin { implementation("com.google.code.gson:gson:2.10.1") implementation("com.arkivanov.decompose:decompose:$decomposeVersion") implementation("com.arkivanov.decompose:extensions-compose:$decomposeVersion") + + implementation("app.cash.sqldelight:coroutines-extensions:2.0.1") } } androidMain { @@ -40,11 +44,7 @@ kotlin { implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0") implementation("androidx.navigation:navigation-compose:2.7.7") - val roomVersion = "2.6.1" - implementation("androidx.room:room-runtime:$roomVersion") - implementation("androidx.room:room-ktx:$roomVersion") - //annotationProcessor("androidx.room:room-compiler:$roomVersion") - //configurations["kapt"].dependencies.add(project.dependencies.create("androidx.room:room-compiler:$roomVersion")) + implementation("app.cash.sqldelight:android-driver:2.0.1") } } jvmMain { @@ -54,6 +54,8 @@ kotlin { val multiplatformSettingsVersion = "1.1.1" implementation("com.russhwolf:multiplatform-settings:$multiplatformSettingsVersion") implementation("com.russhwolf:multiplatform-settings-coroutines:$multiplatformSettingsVersion") + + implementation("app.cash.sqldelight:sqlite-driver:2.0.1") } } } @@ -76,3 +78,11 @@ android { schemaDirectory("$projectDir/schemas/") } } + +sqldelight { + databases { + create("TournamentsDB") { + packageName.set("me.frauenfelderflorian.tournamentscompose") + } + } +} diff --git a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/AndroidApp.kt b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/AndroidApp.kt index 39d3dac..45a911d 100644 --- a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/AndroidApp.kt +++ b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/AndroidApp.kt @@ -24,15 +24,18 @@ import androidx.compose.ui.platform.LocalContext import androidx.core.view.WindowCompat import androidx.lifecycle.asLiveData import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.room.Room import com.arkivanov.decompose.defaultComponentContext import com.arkivanov.decompose.router.stack.StackNavigation import kotlinx.coroutines.launch +import me.frauenfelderflorian.tournamentscompose.common.data.DriverFactory +import me.frauenfelderflorian.tournamentscompose.common.data.GameDao import me.frauenfelderflorian.tournamentscompose.common.data.PlayersModel import me.frauenfelderflorian.tournamentscompose.common.data.Prefs import me.frauenfelderflorian.tournamentscompose.common.data.PrefsFactory -import me.frauenfelderflorian.tournamentscompose.common.data.TournamentsDatabase +import me.frauenfelderflorian.tournamentscompose.common.data.TournamentDao +import me.frauenfelderflorian.tournamentscompose.common.data.TournamentWithGames import me.frauenfelderflorian.tournamentscompose.common.data.TournamentsModel +import me.frauenfelderflorian.tournamentscompose.common.data.createDatabase import me.frauenfelderflorian.tournamentscompose.common.ui.ProvideComponentContext import me.frauenfelderflorian.tournamentscompose.common.ui.Screen import me.frauenfelderflorian.tournamentscompose.common.ui.importFromUri @@ -55,12 +58,19 @@ fun androidApp(activity: ComponentActivity) { fun AndroidAppContent(intent: Intent) { val context = LocalContext.current val prefs: Prefs = viewModel(factory = PrefsFactory(context)).apply { Initialize() } - val db = Room.databaseBuilder(context, TournamentsDatabase::class.java, "tournaments").build() - val tournamentDao = db.tournamentDao() - val gameDao = db.gameDao() + val database = createDatabase(DriverFactory(context)) + val tournamentDao = TournamentDao(database.tournamentQueries) + val gameDao = GameDao(database.gameQueries) val model: TournamentsModel = viewModel() - model.tournaments = tournamentDao.getTournamentsWithGames().asLiveData() - .observeAsState(model.tournaments.values).value.associateBy { it.t.id } + tournamentDao.getTournaments().asLiveData().observeAsState(listOf()).value.associateBy( + keySelector = { it.id }, + valueTransform = { + TournamentWithGames( + t = it, + games = gameDao.getGames(it.id).asLiveData().observeAsState(listOf()).value + ) + }, + ).also { model.tournaments = it } val playersModel: PlayersModel = viewModel() val navigator = remember { StackNavigation() } diff --git a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt index 6be786a..05b4877 100644 --- a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt +++ b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt @@ -1,52 +1,12 @@ package me.frauenfelderflorian.tournamentscompose.common.data -import androidx.room.Dao -import androidx.room.Database -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.RoomDatabase -import androidx.room.Transaction -import androidx.room.Update -import androidx.room.Upsert -import kotlinx.coroutines.flow.Flow - -@Dao -actual interface TournamentDao { - @Transaction - @Query("SELECT * FROM tournament") - actual fun getTournamentsWithGames(): Flow> - - @Insert - actual suspend fun insert(vararg tournaments: Tournament) - - @Update - actual suspend fun update(vararg tournaments: Tournament) - - @Upsert - actual suspend fun upsert(vararg tournaments: Tournament) - - @Delete - actual suspend fun delete(tournament: Tournament) -} - -@Dao -actual interface GameDao { - @Insert - actual suspend fun insert(vararg games: Game) - - @Update - actual suspend fun update(vararg games: Game) - - @Upsert - actual suspend fun upsert(vararg games: Game) - - @Delete - actual suspend fun delete(game: Game) -} - -@Database(entities = [Tournament::class, Game::class], version = 1) -actual abstract class TournamentsDatabase : RoomDatabase() { - actual abstract fun tournamentDao(): TournamentDao - actual abstract fun gameDao(): GameDao +import android.content.Context +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.android.AndroidSqliteDriver +import me.frauenfelderflorian.tournamentscompose.TournamentsDB + +actual class DriverFactory(private val context: Context) { + actual fun createDriver(): SqlDriver { + return AndroidSqliteDriver(TournamentsDB.Schema, context, "TournamentsDB") + } } diff --git a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt index 0ff2644..6d0b496 100644 --- a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt +++ b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt @@ -1,11 +1,6 @@ package me.frauenfelderflorian.tournamentscompose.common.data import androidx.lifecycle.ViewModel -import androidx.room.Embedded -import androidx.room.Entity -import androidx.room.Ignore -import androidx.room.PrimaryKey -import androidx.room.Relation import java.util.UUID actual class TournamentsModel : ViewModel() { @@ -17,43 +12,3 @@ actual class PlayersModel : ViewModel() { actual val players = mutableListOf() actual var edited = false } - -actual data class TournamentWithGames actual constructor( - @Embedded actual val t: Tournament, - @Relation(parentColumn = "id", entityColumn = "tournamentId") actual val games: List, -) { - @Ignore - actual var current: Game? = null -} - -@Entity -actual data class Tournament actual constructor( - @PrimaryKey actual val id: UUID, - actual val name: String, - actual val start: Long, - actual val end: Long, - actual val useAdaptivePoints: Boolean, - actual val firstPoints: Int, - /** - * Concatenated list of all players in this tournament. Use [players] to access. - * - * Leave this empty upon instantiation, and modify it afterwards using [players] - */ - actual var playersString: String, -) - -@Entity -actual data class Game actual constructor( - @PrimaryKey actual val id: UUID, - actual val tournamentId: UUID, - actual val date: Long, - actual val hoops: Int, - actual val hoopReached: Int, - actual val difficulty: String, - /** - * JSON version of the map of the ranking of this game. Use [ranking] to access - * - * Leave this empty upon instantiation, and modify it afterwards using [ranking] - */ - actual var rankingString: String, -) diff --git a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewer.kt b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewer.kt index c9bde70..ae8844f 100644 --- a/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewer.kt +++ b/common/src/androidMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewer.kt @@ -46,8 +46,6 @@ import kotlinx.coroutines.withContext import me.frauenfelderflorian.tournamentscompose.common.data.GameDao import me.frauenfelderflorian.tournamentscompose.common.data.TournamentDao import me.frauenfelderflorian.tournamentscompose.common.data.TournamentWithGames -import me.frauenfelderflorian.tournamentscompose.common.data.players -import me.frauenfelderflorian.tournamentscompose.common.data.ranking import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.stringResource import tournamentscompose.common.generated.resources.* diff --git a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt index 92b4b1e..d8ba6d5 100644 --- a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt +++ b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt @@ -1,23 +1,99 @@ package me.frauenfelderflorian.tournamentscompose.common.data +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import app.cash.sqldelight.db.SqlDriver +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import me.frauenfelderflorian.tournamentscompose.GameQueries +import me.frauenfelderflorian.tournamentscompose.TournamentQueries +import me.frauenfelderflorian.tournamentscompose.TournamentsDB +import java.nio.ByteBuffer +import java.util.UUID -expect interface TournamentDao { - fun getTournamentsWithGames(): Flow> - suspend fun insert(vararg tournaments: Tournament) - suspend fun update(vararg tournaments: Tournament) - suspend fun upsert(vararg tournaments: Tournament) - suspend fun delete(tournament: Tournament) +expect class DriverFactory { + fun createDriver(): SqlDriver } -expect interface GameDao { - suspend fun insert(vararg games: Game) - suspend fun update(vararg games: Game) - suspend fun upsert(vararg games: Game) - suspend fun delete(game: Game) +fun createDatabase(driverFactory: DriverFactory): TournamentsDB { + return TournamentsDB(driverFactory.createDriver()) } -expect abstract class TournamentsDatabase { - abstract fun tournamentDao(): TournamentDao - abstract fun gameDao(): GameDao +class TournamentDao(private val queries: TournamentQueries) { + fun getTournaments(): Flow> { + return queries.getTournaments { id, name, start, end, useAdaptivePoints, firstPoints, playersString -> + Tournament( + id = id.asUuid(), + name = name, + start = start, + end = end, + useAdaptivePoints = useAdaptivePoints.toInt() == 1, + firstPoints = firstPoints.toInt(), + playersString = playersString + ) + }.asFlow().mapToList(Dispatchers.IO) + } + + fun upsert(vararg tournaments: Tournament) { + tournaments.forEach { + queries.upsert( + name = it.name, + start = it.start, + end = it.end, + useAdaptivePoints = it.useAdaptivePoints.toInt().toLong(), + firstPoints = it.firstPoints.toLong(), + playersString = it.playersString, + id = it.id.asByteArray() + ) + } + } + + fun delete(tournament: Tournament) { + queries.delete(tournament.id.asByteArray()) + } } + +class GameDao(private val queries: GameQueries) { + fun getGames(tournamentId: UUID): Flow> { + return queries.getTournamentGames( + tournamentId.asByteArray() + ) { id, tId, date, hoops, hoopReached, difficulty, rankingString -> + Game( + id = id.asUuid(), + tournamentId = tId.asUuid(), + date = date, + hoops = hoops.toInt(), + hoopReached = hoopReached.toInt(), + difficulty = difficulty, + rankingString = rankingString + ) + }.asFlow().mapToList(Dispatchers.IO) + } + + fun upsert(vararg games: Game) { + games.forEach { + queries.upsert( + date = it.date, + hoops = it.hoops.toLong(), + hoopReached = it.hoopReached.toLong(), + difficulty = it.difficulty, + rankingString = it.rankingString, + id = it.id.asByteArray(), + tournamentId = it.tournamentId.asByteArray() + ) + } + } + + fun delete(game: Game) { + queries.delete(game.id.asByteArray()) + } +} + +private fun UUID.asByteArray(): ByteArray = + ByteBuffer.wrap(ByteArray(16)).putLong(this.mostSignificantBits) + .putLong(this.leastSignificantBits).array() + +private fun ByteArray.asUuid(): UUID = + UUID(ByteBuffer.wrap(this).getLong(0), ByteBuffer.wrap(this).getLong(8)) + +private fun Boolean.toInt(): Int = if (this) 1 else 0 diff --git a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt index 5dff8fc..795361f 100644 --- a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt +++ b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt @@ -14,122 +14,102 @@ expect class PlayersModel { var edited: Boolean } -expect class TournamentWithGames(t: Tournament, games: List) { - val t: Tournament - val games: List - var current: Game? -} - -val TournamentWithGames.playersByPoints: List - get() = t.playersString.split(";").sortedByDescending { getPoints(it) } +data class TournamentWithGames constructor( + val t: Tournament, + val games: List, +) { + var current: Game? = null + val playersByPoints: List + get() = t.playersString.split(";").sortedByDescending { getPoints(it) } -fun TournamentWithGames.getPoints(player: String): Int { - var points = 0 - for (game in games) { - var present = 0 - for (player1 in t.playersString.split(";")) { - if (game.ranking[player1]!! > 0) present++ - } - if (t.useAdaptivePoints) { - if (game.ranking[player] == 1) { - points += present + 5 - } else if (game.ranking[player] == 2) { - points += present + 2 - } else if (game.ranking[player] == 3) { - points += present - } else if (game.ranking[player]!! > 3) { - points += present - game.ranking[player]!! + 2 + fun getPoints(player: String): Int { + var points = 0 + for (game in games) { + var present = 0 + for (player1 in t.playersString.split(";")) { + if (game.ranking[player1]!! > 0) present++ } - } else { - if (game.ranking[player] == 0) { - points += t.firstPoints - (present + 5) - } else if (game.ranking[player] == 1) { - points += t.firstPoints - } else if (game.ranking[player] == 2) { - points += t.firstPoints - 3 - } else if (game.ranking[player] == 3) { - points += t.firstPoints - 5 - } else if (game.ranking[player]!! > 3) { - points += t.firstPoints - (game.ranking[player]!! + 3) + if (t.useAdaptivePoints) { + if (game.ranking[player] == 1) { + points += present + 5 + } else if (game.ranking[player] == 2) { + points += present + 2 + } else if (game.ranking[player] == 3) { + points += present + } else if (game.ranking[player]!! > 3) { + points += present - game.ranking[player]!! + 2 + } + } else { + if (game.ranking[player] == 0) { + points += t.firstPoints - (present + 5) + } else if (game.ranking[player] == 1) { + points += t.firstPoints + } else if (game.ranking[player] == 2) { + points += t.firstPoints - 3 + } else if (game.ranking[player] == 3) { + points += t.firstPoints - 5 + } else if (game.ranking[player]!! > 3) { + points += t.firstPoints - (game.ranking[player]!! + 3) + } } } + return points } - return points } -expect class Tournament( - id: UUID, - name: String, - start: Long, - end: Long, - useAdaptivePoints: Boolean, - firstPoints: Int = 10, - playersString: String = "", -) { - val id: UUID - val name: String - val start: Long - val end: Long - val useAdaptivePoints: Boolean - val firstPoints: Int - +data class Tournament constructor( + val id: UUID, + val name: String, + val start: Long, + val end: Long, + val useAdaptivePoints: Boolean, + val firstPoints: Int = 10, /** * Concatenated list of all players in this tournament. Use [players] to access. * * Leave this empty upon instantiation, and modify it afterwards using [players] */ - var playersString: String -} - -/** - * List of all players in this tournament. Use this to modify or read [Tournament.playersString] - */ -var Tournament.players: List - get() = playersString.split(";") - set(value) { - playersString = value.joinToString(";") - } - - -expect class Game( - id: UUID, - tournamentId: UUID, - date: Long, - hoops: Int, - hoopReached: Int, - difficulty: String, - rankingString: String = "", + var playersString: String = "", ) { - val id: UUID - val tournamentId: UUID - val date: Long - val hoops: Int - val hoopReached: Int - val difficulty: String + /** + * List of all players in this tournament. Use this to modify or read [Tournament.playersString] + */ + var players: List + get() = playersString.split(";") + set(value) { + playersString = value.joinToString(";") + } +} +data class Game constructor( + val id: UUID, + val tournamentId: UUID, + val date: Long, + val hoops: Int, + val hoopReached: Int, + val difficulty: String, /** * JSON version of the map of the ranking of this game. Use [ranking] to access * * Leave this empty upon instantiation, and modify it afterwards using [ranking] */ - var rankingString: String -} - -/** - * Map of the ranking of this game. Use this to modify or read [Game.rankingString] - */ -var Game.ranking: Map - get() = gson.fromJson(rankingString, object : TypeToken>() {}.type) - set(value) { - rankingString = gson.toJson(value) - } -val Game.absentPlayers get() = ranking.filterValues { it == 0 }.keys -val Game.playersByRank: List - get() { - val players = mutableListOf() - for (i in 0 until ranking.size - absentPlayers.size) { - players.add(ranking.filterValues { it == i + 1 }.keys.first()) + var rankingString: String = "", +) { + /** + * Map of the ranking of this game. Use this to modify or read [Game.rankingString] + */ + var ranking: Map + get() = gson.fromJson(rankingString, object : TypeToken>() {}.type) + set(value) { + rankingString = gson.toJson(value) } - return players.toList() - } - + val absentPlayers get() = ranking.filterValues { it == 0 }.keys + val playersByRank: List + get() { + val players = mutableListOf() + for (i in 0 until ranking.size - absentPlayers.size) { + players.add(ranking.filterValues { it == i + 1 }.keys.first()) + } + return players.toList() + } +} diff --git a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameEditor.kt b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameEditor.kt index 69888e4..6cd02ac 100644 --- a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameEditor.kt +++ b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameEditor.kt @@ -71,9 +71,6 @@ import kotlinx.coroutines.withContext import me.frauenfelderflorian.tournamentscompose.common.data.Game import me.frauenfelderflorian.tournamentscompose.common.data.GameDao import me.frauenfelderflorian.tournamentscompose.common.data.TournamentWithGames -import me.frauenfelderflorian.tournamentscompose.common.data.players -import me.frauenfelderflorian.tournamentscompose.common.data.playersByRank -import me.frauenfelderflorian.tournamentscompose.common.data.ranking import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.stringResource import tournamentscompose.common.generated.resources.* diff --git a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameViewer.kt b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameViewer.kt index 7e05037..08bf57a 100644 --- a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameViewer.kt +++ b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/GameViewer.kt @@ -32,9 +32,6 @@ import androidx.compose.ui.unit.sp import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.push import me.frauenfelderflorian.tournamentscompose.common.data.Game -import me.frauenfelderflorian.tournamentscompose.common.data.absentPlayers -import me.frauenfelderflorian.tournamentscompose.common.data.playersByRank -import me.frauenfelderflorian.tournamentscompose.common.data.ranking import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.stringResource import tournamentscompose.common.generated.resources.* diff --git a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/PlayerViewer.kt b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/PlayerViewer.kt index cb1061a..e093aa4 100644 --- a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/PlayerViewer.kt +++ b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/PlayerViewer.kt @@ -1,6 +1,5 @@ package me.frauenfelderflorian.tournamentscompose.common.ui - import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -22,8 +21,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.unit.dp import com.arkivanov.decompose.router.stack.StackNavigation import me.frauenfelderflorian.tournamentscompose.common.data.TournamentWithGames -import me.frauenfelderflorian.tournamentscompose.common.data.playersByRank -import me.frauenfelderflorian.tournamentscompose.common.data.ranking import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.stringResource import tournamentscompose.common.generated.resources.* diff --git a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentEditor.kt b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentEditor.kt index 8b1105d..45697c4 100644 --- a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentEditor.kt +++ b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentEditor.kt @@ -62,7 +62,6 @@ import me.frauenfelderflorian.tournamentscompose.common.data.Prefs import me.frauenfelderflorian.tournamentscompose.common.data.Tournament import me.frauenfelderflorian.tournamentscompose.common.data.TournamentDao import me.frauenfelderflorian.tournamentscompose.common.data.TournamentWithGames -import me.frauenfelderflorian.tournamentscompose.common.data.players import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.stringResource import tournamentscompose.common.generated.resources.* diff --git a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewerContent.kt b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewerContent.kt index a8254d5..691a297 100644 --- a/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewerContent.kt +++ b/common/src/commonMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/ui/TournamentViewerContent.kt @@ -42,8 +42,6 @@ import com.arkivanov.decompose.router.stack.push import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import me.frauenfelderflorian.tournamentscompose.common.data.TournamentWithGames -import me.frauenfelderflorian.tournamentscompose.common.data.getPoints -import me.frauenfelderflorian.tournamentscompose.common.data.playersByPoints import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.stringResource import tournamentscompose.common.generated.resources.* diff --git a/common/src/commonMain/sqldelight/me/frauenfelderflorian/tournamentscompose/Game.sq b/common/src/commonMain/sqldelight/me/frauenfelderflorian/tournamentscompose/Game.sq new file mode 100644 index 0000000..f20d111 --- /dev/null +++ b/common/src/commonMain/sqldelight/me/frauenfelderflorian/tournamentscompose/Game.sq @@ -0,0 +1,24 @@ +CREATE TABLE Game ( + id BLOB PRIMARY KEY NOT NULL, + tournamentId BLOB NOT NULL, + date INTEGER NOT NULL, + hoops INTEGER NOT NULL, + hoopReached INTEGER NOT NULL, + difficulty TEXT NOT NULL, + rankingString TEXT NOT NULL +); + +getTournamentGames: +SELECT * FROM Game WHERE tournamentId = :tournamentId; + +upsert { + UPDATE Game SET date = :date, hoops = :hoops, hoopReached = :hoopReached, + difficulty = :difficulty, rankingString = :rankingString WHERE id = :id; + + INSERT OR IGNORE INTO Game + (id, tournamentId, date, hoops, hoopReached, difficulty, rankingString) + VALUES(:id, :tournamentId, :date, :hoops, :hoopReached, :difficulty, :rankingString); +} + +delete: +DELETE FROM Game WHERE id = :id; diff --git a/common/src/commonMain/sqldelight/me/frauenfelderflorian/tournamentscompose/Tournament.sq b/common/src/commonMain/sqldelight/me/frauenfelderflorian/tournamentscompose/Tournament.sq new file mode 100644 index 0000000..2771687 --- /dev/null +++ b/common/src/commonMain/sqldelight/me/frauenfelderflorian/tournamentscompose/Tournament.sq @@ -0,0 +1,25 @@ +CREATE TABLE Tournament ( + id BLOB PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + start INTEGER NOT NULL, + end INTEGER NOT NULL, + useAdaptivePoints INTEGER NOT NULL, + firstPoints INTEGER NOT NULL, + playersString TEXT NOT NULL +); + +getTournaments: +SELECT * FROM Tournament; + +upsert { + UPDATE Tournament SET name = :name, start = :start, end = :end, + useAdaptivePoints = :useAdaptivePoints, firstPoints = :firstPoints, + playersString = :playersString WHERE id = :id; + + INSERT OR IGNORE INTO Tournament + (id, name, start, end, useAdaptivePoints, firstPoints, playersString) + VALUES(:id, :name, :start, :end, :useAdaptivePoints, :firstPoints, :playersString); +} + +delete: +DELETE FROM Tournament WHERE id = :id; diff --git a/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt b/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt index 1abf4b4..c8f46ee 100644 --- a/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt +++ b/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Database.kt @@ -1,63 +1,13 @@ package me.frauenfelderflorian.tournamentscompose.common.data -import kotlinx.coroutines.flow.Flow +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import me.frauenfelderflorian.tournamentscompose.TournamentsDB -actual interface TournamentDao { - actual fun getTournamentsWithGames(): Flow> - actual suspend fun insert(vararg tournaments: Tournament) - actual suspend fun update(vararg tournaments: Tournament) - actual suspend fun upsert(vararg tournaments: Tournament) - actual suspend fun delete(tournament: Tournament) -} - -class TournamentDaoTest : TournamentDao { - override fun getTournamentsWithGames(): Flow> { - TODO("Not yet implemented") - } - - override suspend fun insert(vararg tournaments: Tournament) { - TODO("Not yet implemented") - } - - override suspend fun update(vararg tournaments: Tournament) { - TODO("Not yet implemented") +actual class DriverFactory { + actual fun createDriver(): SqlDriver { + val driver: SqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + TournamentsDB.Schema.create(driver) + return driver } - - override suspend fun upsert(vararg tournaments: Tournament) { - TODO("Not yet implemented") - } - - override suspend fun delete(tournament: Tournament) { - TODO("Not yet implemented") - } -} - -actual interface GameDao { - actual suspend fun insert(vararg games: Game) - actual suspend fun update(vararg games: Game) - actual suspend fun upsert(vararg games: Game) - actual suspend fun delete(game: Game) -} - -class GameDaoTest : GameDao { - override suspend fun insert(vararg games: Game) { - TODO("Not yet implemented") - } - - override suspend fun update(vararg games: Game) { - TODO("Not yet implemented") - } - - override suspend fun upsert(vararg games: Game) { - TODO("Not yet implemented") - } - - override suspend fun delete(game: Game) { - TODO("Not yet implemented") - } -} - -actual abstract class TournamentsDatabase { - actual abstract fun tournamentDao(): TournamentDao - actual abstract fun gameDao(): GameDao } diff --git a/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt b/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt index d0f7190..7dd0235 100644 --- a/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt +++ b/common/src/jvmMain/kotlin/me/frauenfelderflorian/tournamentscompose/common/data/Model.kt @@ -11,40 +11,3 @@ actual class PlayersModel { actual val players = mutableListOf() actual var edited = false } - -actual data class TournamentWithGames actual constructor( - actual val t: Tournament, - actual val games: List, -) { - actual var current: Game? = null -} - -actual data class Tournament actual constructor( - actual val id: UUID, - actual val name: String, - actual val start: Long, - actual val end: Long, - actual val useAdaptivePoints: Boolean, - actual val firstPoints: Int, - /** - * Concatenated list of all players in this tournament. Use [players] to access. - * - * Leave this empty upon instantiation, and modify it afterwards using [players] - */ - actual var playersString: String, -) - -actual data class Game actual constructor( - actual val id: UUID, - actual val tournamentId: UUID, - actual val date: Long, - actual val hoops: Int, - actual val hoopReached: Int, - actual val difficulty: String, - /** - * JSON version of the map of the ranking of this game. Use [ranking] to access - * - * Leave this empty upon instantiation, and modify it afterwards using [ranking] - */ - actual var rankingString: String, -)