commit 7312359f39bdb00542f5580353554ed97b440465 parent a83a25e5eb94a7dcfaa36a5e49311d3d1bf782c0 Author: Jean Schurger <jean@schurger.org> Date: Sat, 17 Oct 2026 16:10:38 -0400 Version 1.3: the library shows the number of marks of each track Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141Zm4UQq8PATQAG98MdNCc Diffstat:
8 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "org.schurger.sonodrill" minSdk = 26 targetSdk = 35 - versionCode = 3 - versionName = "1.2.0" + versionCode = 4 + versionName = "1.3.0" } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 diff --git a/composeApp/src/androidUnitTest/kotlin/ubi2/sonodrill/PlayerViewModelTest.kt b/composeApp/src/androidUnitTest/kotlin/ubi2/sonodrill/PlayerViewModelTest.kt @@ -226,6 +226,17 @@ class RepositoryTest { } @Test + fun the_library_counts_the_marks_of_each_track() { + val repo = new_repo() + val song = repo.register_file("audio/song.mp3") + val other = repo.register_file("audio/other.mp3") + repo.insert_mark(MarkInfo(id = "a", file_id = song, position_ms = 1000)) + repo.insert_mark(MarkInfo(id = "b", file_id = song, position_ms = 2000)) + val counts = repo.get_all_files().associate { it.id to it.mark_count } + assertEquals(mapOf(song to 2, other to 0), counts) + } + + @Test fun marks_go_through_icloud_to_another_device() { val cloud = FakeCloud() val phone = new_repo(cloud) diff --git a/composeApp/src/commonMain/kotlin/ubi2/sonodrill/data/repository.kt b/composeApp/src/commonMain/kotlin/ubi2/sonodrill/data/repository.kt @@ -22,8 +22,10 @@ class AudioRepository( private val zone_queries get() = db.zoneQueries private val pref_queries get() = db.preferenceQueries - fun get_all_files(): List<AudioFileInfo> = - queries.select_all().executeAsList().map { it.to_info() } + fun get_all_files(): List<AudioFileInfo> { + val mark_counts = mark_queries.count_by_file().executeAsList().associate { it.file_id to it.mark_count.toInt() } + return queries.select_all().executeAsList().map { it.to_info().copy(mark_count = mark_counts[it.id] ?: 0) } + } fun get_file(id: String): AudioFileInfo? = queries.select_by_id(id).executeAsOneOrNull()?.to_info() diff --git a/composeApp/src/commonMain/kotlin/ubi2/sonodrill/model/models.kt b/composeApp/src/commonMain/kotlin/ubi2/sonodrill/model/models.kt @@ -14,6 +14,8 @@ data class AudioFileInfo( val low_cut: Boolean = false, // Last change to the marks or zones, to sync them through iCloud val marks_updated_at: Long = 0, + // Not stored: number of marks, shown in the library + val mark_count: Int = 0, // Not stored: set when the audio file can't be found on disk val missing: Boolean = false, ) diff --git a/composeApp/src/commonMain/kotlin/ubi2/sonodrill/ui/library/library_screen.kt b/composeApp/src/commonMain/kotlin/ubi2/sonodrill/ui/library/library_screen.kt @@ -88,7 +88,11 @@ private fun AudioFileItem(file: AudioFileInfo, on_click: () -> Unit, on_delete: Column(Modifier.weight(1f)) { Text(file.title, maxLines = 1, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.bodyLarge) if (file.missing) Text(S.file_not_found, fontSize = 13.sp, color = MaterialTheme.colorScheme.error) - else file.duration_ms?.let { Text(format_time(it), fontSize = 13.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) } + else { + // Duration, then the number of marks: tracks already prepared stand out + val details = listOfNotNull(file.duration_ms?.let { format_time(it) }, file.mark_count.takeIf { it > 0 }?.let { S.marks_count(it) }) + if (details.isNotEmpty()) Text(details.joinToString(" · "), fontSize = 13.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) + } } if (file.last_position_ms > 0 && file.duration_ms != null && file.duration_ms > 0) LinearProgressIndicator(progress = { file.last_position_ms.toFloat() / file.duration_ms }, modifier = Modifier.width(40.dp).padding(end = 8.dp)) diff --git a/composeApp/src/commonMain/kotlin/ubi2/sonodrill/ui/strings.kt b/composeApp/src/commonMain/kotlin/ubi2/sonodrill/ui/strings.kt @@ -66,6 +66,7 @@ class Strings( val apply_marks_to: String, val marks_from: (String) -> String, val marks_added: (Int) -> String, + val marks_count: (Int) -> String, val invalid_marks_file: String, val no_tracks: String, ) @@ -91,6 +92,7 @@ private val en = Strings( from = "From", up_to = "Up to", step = "Step", apply_marks_to = "Apply the marks to", marks_from = { "Marks of \"$it\"" }, marks_added = { if (it == 1) "1 mark added" else "$it marks added" }, + marks_count = { if (it == 1) "1 mark" else "$it marks" }, invalid_marks_file = "This file doesn't contain Sonodrill marks", no_tracks = "Import the track first", ) @@ -118,6 +120,7 @@ private val fr = Strings( from = "De", up_to = "Jusqu'à", step = "Palier", apply_marks_to = "Appliquer les repères à", marks_from = { "Repères de « $it »" }, marks_added = { if (it <= 1) "$it repère ajouté" else "$it repères ajoutés" }, + marks_count = { if (it <= 1) "$it repère" else "$it repères" }, invalid_marks_file = "Ce fichier ne contient pas de repères Sonodrill", no_tracks = "Importez d'abord le morceau", ) diff --git a/composeApp/src/commonMain/sqldelight/ubi2/sonodrill/data/Mark.sq b/composeApp/src/commonMain/sqldelight/ubi2/sonodrill/data/Mark.sq @@ -8,6 +8,9 @@ CREATE TABLE mark ( FOREIGN KEY(file_id) REFERENCES audio_file(id) ON DELETE CASCADE ); +count_by_file: +SELECT file_id, COUNT(*) AS mark_count FROM mark GROUP BY file_id; + select_by_file: SELECT id, file_id, position_ms, label, created_at FROM mark WHERE file_id = ? ORDER BY position_ms ASC; diff --git a/iosApp/project.yml b/iosApp/project.yml @@ -25,7 +25,7 @@ targets: info: path: Info.plist properties: - CFBundleShortVersionString: "1.2" + CFBundleShortVersionString: "1.3" CFBundleDevelopmentRegion: en CFBundleLocalizations: - en