From 13451a9955722f2a6346c5263921564c8a97ef14 Mon Sep 17 00:00:00 2001 From: RallyBlock <50393307+RallyBlock@users.noreply.github.com> Date: Thu, 22 Oct 2026 21:11:56 -0700 Subject: [PATCH] Start with no cars so the first one is added on purpose. --- README.md | 2 + app/build.gradle.kts | 4 +- .../com/racingapp/pitlog/data/AppDatabase.kt | 13 ------- .../java/com/racingapp/pitlog/data/RaceDao.kt | 6 +++ .../com/racingapp/pitlog/data/Repository.kt | 38 +++++++++--------- .../racingapp/pitlog/ui/home/HomeScreen.kt | 39 ++++++++++++++++++- .../pitlog/ui/settings/SettingsScreen.kt | 17 +++++++- .../racingapp/pitlog/PreviewScreenshotTest.kt | 8 ++++ 8 files changed, 90 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index c4b3090..9b8852e 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Pressures are PSI. Tire heat is °F, read inside, middle, then outside. ## On the phone +The phone starts with no cars. Add one from the home screen, or paste a token from another phone. + The gear icon opens **Car**. A new install already has the server address filled in: `http://sync.sleepingliongames.com:27017` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index eaaedf7..e400614 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.racingapp.pitlog" minSdk = 26 targetSdk = 36 - versionCode = 2 - versionName = "1.1" + versionCode = 3 + versionName = "1.2" } buildTypes { diff --git a/app/src/main/java/com/racingapp/pitlog/data/AppDatabase.kt b/app/src/main/java/com/racingapp/pitlog/data/AppDatabase.kt index 01f4e64..93b9916 100644 --- a/app/src/main/java/com/racingapp/pitlog/data/AppDatabase.kt +++ b/app/src/main/java/com/racingapp/pitlog/data/AppDatabase.kt @@ -246,19 +246,6 @@ abstract class AppDatabase : RoomDatabase() { fun create(context: Context): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, "pitlog.db") .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9) - .addCallback( - object : Callback() { - override fun onCreate(db: SupportSQLiteDatabase) { - super.onCreate(db) - db.execSQL( - """ - INSERT INTO cars (carName, raceClass, discipline, token, updatedAt, syncRev, deleted, pending) - VALUES ('$DEFAULT_CAR', '$DEFAULT_CLASS', '$DEFAULT_DISCIPLINE', printf('%08d', abs(random()) % 100000000), 1, 0, 0, 1) - """.trimIndent(), - ) - } - }, - ) .build() } } diff --git a/app/src/main/java/com/racingapp/pitlog/data/RaceDao.kt b/app/src/main/java/com/racingapp/pitlog/data/RaceDao.kt index 39ae11a..e722bd0 100644 --- a/app/src/main/java/com/racingapp/pitlog/data/RaceDao.kt +++ b/app/src/main/java/com/racingapp/pitlog/data/RaceDao.kt @@ -123,6 +123,12 @@ interface RaceDao { @Query("SELECT * FROM cars ORDER BY id ASC") suspend fun everyCar(): List + @Query("SELECT COUNT(*) FROM race_nights WHERE carId = :carId AND deleted = 0") + suspend fun nightCount(carId: Long): Int + + @Query("SELECT COUNT(*) FROM setup_sheets WHERE carId = :carId AND deleted = 0") + suspend fun sheetCount(carId: Long): Int + @Query("SELECT * FROM cars WHERE deleted = 0 ORDER BY id ASC") suspend fun activeCars(): List diff --git a/app/src/main/java/com/racingapp/pitlog/data/Repository.kt b/app/src/main/java/com/racingapp/pitlog/data/Repository.kt index f285ce3..7ccb834 100644 --- a/app/src/main/java/com/racingapp/pitlog/data/Repository.kt +++ b/app/src/main/java/com/racingapp/pitlog/data/Repository.kt @@ -10,22 +10,18 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map suspend fun ensureCars(dao: RaceDao, preferences: SyncPreferences) { - val cars = dao.everyCar() - if (cars.isEmpty()) { - val id = dao.insertCar( - Car( - carName = DEFAULT_CAR, - raceClass = DEFAULT_CLASS, - discipline = DEFAULT_DISCIPLINE, - token = newCarToken(), - updatedAt = 1, - pending = true, - ), - ) - preferences.selectedCarId = id - return - } - for (car in cars) { + for (car in dao.everyCar()) { + if (car.deleted) continue + val untouched = car.updatedAt == 1L && + car.carName == DEFAULT_CAR && + car.raceClass == DEFAULT_CLASS && + car.discipline == DEFAULT_DISCIPLINE && + dao.nightCount(car.id) == 0 && + dao.sheetCount(car.id) == 0 + if (untouched) { + dao.updateCar(car.copy(deleted = true, pending = false)) + continue + } if (isEightDigitToken(car.token)) continue val source = car.token.ifBlank { preferences.token.trim() } val next = if (source.isBlank()) newCarToken() else eightDigitToken(source) @@ -34,10 +30,14 @@ suspend fun ensureCars(dao: RaceDao, preferences: SyncPreferences) { dao.markRunsPending(car.id) dao.markSheetsPending(car.id) } - val first = cars.firstOrNull { !it.deleted } ?: cars.first() + val active = dao.activeCars() + if (active.isEmpty()) { + preferences.selectedCarId = 0 + return + } val selected = preferences.selectedCarId - if (selected == 0L || cars.none { it.id == selected && !it.deleted }) { - preferences.selectedCarId = first.id + if (selected == 0L || active.none { it.id == selected }) { + preferences.selectedCarId = active.first().id } } diff --git a/app/src/main/java/com/racingapp/pitlog/ui/home/HomeScreen.kt b/app/src/main/java/com/racingapp/pitlog/ui/home/HomeScreen.kt index 932f80f..5534b77 100644 --- a/app/src/main/java/com/racingapp/pitlog/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/racingapp/pitlog/ui/home/HomeScreen.kt @@ -150,8 +150,15 @@ fun HomeScreen( IconButton(onClick = onSettings) { Icon(Icons.Filled.Settings, contentDescription = "Car") } - IconButton(onClick = onNewNight) { - Icon(Icons.Filled.Add, contentDescription = "New race night") + IconButton( + onClick = { + if (state.cars.isEmpty()) viewModel.addCar(onSettings) else onNewNight() + }, + ) { + Icon( + Icons.Filled.Add, + contentDescription = if (state.cars.isEmpty()) "Add a car" else "New race night", + ) } }, ) @@ -166,6 +173,9 @@ fun HomeScreen( modifier = Modifier.fillMaxSize(), contentPadding = padding, ) { + if (state.cars.isEmpty()) { + item { EmptyGarage(onAddCar = { viewModel.addCar(onSettings) }) } + } else { item { HomeHeader( profile = state.profile, @@ -193,6 +203,7 @@ fun HomeScreen( NightCard(night, onClick = { onOpenNight(night.night.id) }) } } + } } } } @@ -283,6 +294,29 @@ private fun HomeHeader( } } +@Composable +private fun EmptyGarage(onAddCar: () -> Unit) { + Column(Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) { + Text( + "No car yet", + style = MaterialTheme.typography.titleLarge, + ) + Text( + "Add a car to start a log. Each car gets its own 8-digit token.", + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(top = 8.dp), + ) + Button( + onClick = onAddCar, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + ) { + Text("Add a car") + } + } +} + @Composable private fun EmptyNights() { Column(Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) { @@ -366,3 +400,4 @@ private fun CarMark() { ) } } + \ No newline at end of file diff --git a/app/src/main/java/com/racingapp/pitlog/ui/settings/SettingsScreen.kt b/app/src/main/java/com/racingapp/pitlog/ui/settings/SettingsScreen.kt index 035b668..b2a1e7c 100644 --- a/app/src/main/java/com/racingapp/pitlog/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/racingapp/pitlog/ui/settings/SettingsScreen.kt @@ -200,7 +200,7 @@ fun SettingsScreen(onBack: () -> Unit) { ) }, bottomBar = { - if (viewModel.ready) { + if (viewModel.ready && viewModel.carId != 0L) { PitActionButton( text = if (viewModel.saving) "Saving…" else "Save", onClick = { viewModel.save(onBack) }, @@ -215,6 +215,20 @@ fun SettingsScreen(onBack: () -> Unit) { } } else { PitScroll(padding) { + if (viewModel.carId == 0L) { + Text( + "Add a car before you log a night. Pressures are PSI and tire heat is °F.", + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + OutlinedButton( + onClick = viewModel::addCar, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp), + ) { + Text("Add a car") + } + } else { Text( "New race nights start with this car. Pressures are PSI and tire heat is °F.", color = MaterialTheme.colorScheme.onSurfaceVariant, @@ -276,6 +290,7 @@ fun SettingsScreen(onBack: () -> Unit) { ) { Text("Add a car") } + } SectionLabel("Add a car from a token") WordsField( label = "Token from another phone", diff --git a/app/src/test/java/com/racingapp/pitlog/PreviewScreenshotTest.kt b/app/src/test/java/com/racingapp/pitlog/PreviewScreenshotTest.kt index 9e3dd0f..3fd8597 100644 --- a/app/src/test/java/com/racingapp/pitlog/PreviewScreenshotTest.kt +++ b/app/src/test/java/com/racingapp/pitlog/PreviewScreenshotTest.kt @@ -33,6 +33,14 @@ class PreviewApp : RacingApp() { super.onCreate() repository.saveAddress("") runBlocking(Dispatchers.IO) { + val car = repository.addCar() + repository.saveCar( + car.copy( + carName = DEFAULT_CAR, + raceClass = DEFAULT_CLASS, + discipline = DEFAULT_DISCIPLINE, + ), + ) val nightId = repository.saveNight( RaceNight( dateEpochDay = LocalDate.now().toEpochDay(),