feat: Reports, Duplicates and Suspect sources live under Collection, with their counts on its rail and a "To look at" line naming the ones waiting; notices link there (M504 #5625)
release / govulncheck (push) Successful in 21s
release / web (push) Successful in 1m21s
release / go (push) Successful in 1m39s
release / integration (push) Successful in 5m31s
release / android (push) Successful in 6m5s
release / Build signed APK (releases and dev) (push) Successful in 6m21s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 32s
release / Verify release artifacts (tag releases only) (push) Skipped

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-11 14:35:59 -04:00
co-authored by Claude Opus 5.5
parent 12725e71b0
commit 38b2325d2d
17 changed files with 114 additions and 53 deletions
@@ -9,18 +9,23 @@ import com.fabledsword.minstrel.nav.Requests
/**
* The app screen for a notice's link. The server writes web paths, the same
* for every client; this maps them onto the app's routes. Admin pages the app
* has no screen for (missing files, duplicates, playback errors) open the
* Admin landing; anything unknown opens nothing.
* for every client; this maps them onto the app's routes. The library's
* health pages sit under Collection on the web (M504): Reports opens the
* app's report screen, and the ones the app has no screen for (duplicates,
* suspect sources, missing files) open the Admin landing, as do other Admin
* pages; anything unknown opens nothing.
*/
private val ADMIN_COLLECTION_PAGES = setOf("duplicates", "suspect-sources", "missing")
internal fun routeForLink(link: String): Any? {
val parts = link.trim('/').split('/').filter { it.isNotEmpty() }
val parts = link.substringBefore('?').trim('/').split('/').filter { it.isNotEmpty() }
return when {
parts.size == 2 && parts[0] == "albums" -> AlbumDetail(parts[1])
parts.size == 2 && parts[0] == "artists" -> ArtistDetail(parts[1])
parts == listOf("requests") -> Requests
parts == listOf("admin", "requests") -> AdminRequests
parts == listOf("admin", "quarantine") -> AdminQuarantine
parts == listOf("collection", "reports") -> AdminQuarantine
parts.firstOrNull() == "collection" && parts.getOrNull(1) in ADMIN_COLLECTION_PAGES -> Admin
parts.firstOrNull() == "admin" -> Admin
else -> null
}
@@ -23,9 +23,11 @@ class NotificationUiTest {
assertEquals(ArtistDetail("ar-1"), routeForLink("/artists/ar-1"))
assertEquals(Requests, routeForLink("/requests"))
assertEquals(AdminRequests, routeForLink("/admin/requests"))
assertEquals(AdminQuarantine, routeForLink("/admin/quarantine"))
// Admin pages the app has no screen for open the Admin landing.
assertEquals(Admin, routeForLink("/admin/duplicates"))
assertEquals(AdminQuarantine, routeForLink("/collection/reports"))
// Library health pages the app has no screen for open the Admin landing.
assertEquals(Admin, routeForLink("/collection/duplicates"))
assertEquals(Admin, routeForLink("/collection/duplicates?view=resolved"))
assertEquals(Admin, routeForLink("/admin/missing-files"))
assertEquals(Admin, routeForLink("/admin"))
assertNull(routeForLink("/somewhere-new"))
}