diff --git a/app/androidApp/src/main/kotlin/com/example/aiapp/SessionUsageBar.kt b/app/androidApp/src/main/kotlin/com/example/aiapp/SessionUsageBar.kt index 9360669..ff566aa 100644 --- a/app/androidApp/src/main/kotlin/com/example/aiapp/SessionUsageBar.kt +++ b/app/androidApp/src/main/kotlin/com/example/aiapp/SessionUsageBar.kt @@ -27,6 +27,17 @@ sealed class FiveHourUsage { data class Known(val percent: Double, val resetsAt: String?) : FiveHourUsage() + /** + * This machine meters nothing, so there is no window to show. + * + * Separate from [Unavailable], and the distinction is the whole point: a session on `echo` or + * on a local llama.cpp has no paid quota at all, which is a fact about how it was set up and + * not a failure to find something out. The backend never asks such a machine, so it returns no + * snapshot for it -- and reading that silence as "couldn't find out" is exactly the mistake of + * answering with the nearest available word. Drawn as nothing, because there is nothing. + */ + data object NotMetered : FiveHourUsage() + /** * The question could not be answered, and why. * @@ -69,11 +80,18 @@ fun SessionUsageBar(settings: ServerSettings, setup: String, modifier: Modifier } } + // Nothing at all for a machine that meters nothing: a row saying "unknown" there would + // report a problem about a setup somebody chose, on every screen, forever. + if (usage is FiveHourUsage.NotMetered) { + return + } + Row( verticalAlignment = Alignment.CenterVertically, modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 2.dp), ) { when (val state = usage) { + FiveHourUsage.NotMetered -> Unit // Words, not a colour and not an empty bar: "couldn't check" is a different kind of // answer from "this much is used", and only words carry a difference in kind. is FiveHourUsage.Unavailable -> @@ -111,14 +129,18 @@ fun SessionUsageBar(settings: ServerSettings, setup: String, modifier: Modifier * the label is written to be read and would stop matching the day its wording changes, silently * leaving the bar with nothing to show. * - * Every way of not having a number is [FiveHourUsage.Unavailable] with the reason in it: a machine - * nobody logged into, one that could not be reached, a snapshot that came back without the window. - * None of them may look like zero. + * Every way of having *failed* to get a number is [FiveHourUsage.Unavailable] with the reason in + * it: a machine nobody logged into, one that could not be reached, a snapshot that came back + * without the window. None of them may look like zero, and none of them may look like + * [FiveHourUsage.NotMetered], which is the machine having no quota rather than the question going + * unanswered. */ fun fiveHourFor(snapshots: List, setup: String): FiveHourUsage { val mine = snapshots.firstOrNull { it.setup == setup } + // No snapshot at all means the backend never asked, which it only does for a machine with + // nothing metered on it. That is a different answer from having asked and failed. if (mine == null) { - return FiveHourUsage.Unavailable("this machine reports no usage") + return FiveHourUsage.NotMetered } if (mine.state != "ok") { return FiveHourUsage.Unavailable(mine.detail ?: mine.state)