The same pass the server had, on the Kotlin side: comments restating what the code says are gone, and the ones recording a measurement, a constraint or an incident are kept but cut to a few lines each. 6540 comment lines to 5674, and 920 lines off the app. Two doc comments had drifted onto the item above the one they describe -- `contextAfter`'s onto `sessionWorking` in Events.kt, and `UsageMonitor`'s equivalent on the server was fixed in the previous commit. Each is back on its own item, which is the only non-comment line this diff moves. The comments are reflowed to the column limit at their own indentation: several were written wide, and ktfmt re-wrapped them into lines holding a single orphan word. `/tmp` script, not kept -- ktfmt is idempotent over the result, which is the check. Left alone deliberately: this codebase's remaining comment density is high because the comments carry things the code cannot say -- what a null means, what a number was measured against, which bug a guard exists for. Of the 238 one-line doc comments in the app, five were pure restatement of the name and were removed; the rest each say something the signature does not. ktfmtFormat, compileDebugKotlin, lintDebug and testDebugUnitTest pass; cargo test (127), clippy --all-targets and fmt still clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
2.8 KiB
Kotlin
65 lines
2.8 KiB
Kotlin
package com.example.aiapp
|
|
|
|
import java.time.Duration
|
|
import java.time.OffsetDateTime
|
|
|
|
// How long is left in a usage window. Shared by the session bar and the usage screen: the
|
|
// arithmetic is the same in both, so everything here returns the span or the state on its own and
|
|
// leaves the wording to the caller.
|
|
|
|
/**
|
|
* "1d 4h", "3h 12m", "12m" -- the span alone, with no leading or trailing words.
|
|
*
|
|
* Rounded **up** to the whole minute, rather than truncated as it was. A window with 3h 12m 50s
|
|
* left is nearer four minutes past the twelve than it is to twelve, and truncating also parks the
|
|
* figure on a minute it has already spent. One rule, so the session bar and the usage dialog cannot
|
|
* round a shared measurement two different ways.
|
|
*/
|
|
fun formatSpan(until: Duration): String {
|
|
val up = if (until.seconds % 60 == 0L && until.nano == 0) until else until.plusMinutes(1)
|
|
return when {
|
|
up.toHours() >= 24 -> "${up.toDays()}d ${up.toHours() % 24}h"
|
|
up.toHours() > 0 -> "${up.toHours()}h ${up.toMinutes() % 60}m"
|
|
else -> "${up.toMinutes()}m"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* What is known about when a usage window ends.
|
|
*
|
|
* Three answers rather than a nullable duration, because two of them shared `null` and they are not
|
|
* the same thing at all. A window the server sent no reset time for is one that is **not running**:
|
|
* the five-hour window is anchored to the block it started in, so between sessions there is nothing
|
|
* counting down and the API says so by omitting the field. A timestamp that did arrive and could
|
|
* not be read is the genuinely unknown case.
|
|
*
|
|
* Collapsing them put "reset time unknown" on the session bar for a machine behaving perfectly, on
|
|
* the one row somebody reads before starting something big -- and the usage dialog, looking at the
|
|
* same field, quietly drew nothing.
|
|
*/
|
|
sealed class WindowEnd {
|
|
/** No reset time was sent, so nothing is running in this window. Not a failure to find out. */
|
|
data object NotRunning : WindowEnd()
|
|
|
|
/** A timestamp arrived and could not be read. The one case that is actually unknown. */
|
|
data object Unreadable : WindowEnd()
|
|
|
|
/** How long is left. Negative once the window is past, which each caller words for itself. */
|
|
data class Ends(val until: Duration) : WindowEnd()
|
|
}
|
|
|
|
/**
|
|
* [resetsAt] as the server sent it -- absent, unreadable, or a moment -- against [now].
|
|
*
|
|
* [now] is a parameter rather than read here so a caller can drive it from state and have the
|
|
* countdown recompute on its own schedule.
|
|
*/
|
|
fun windowEnd(resetsAt: String?, now: OffsetDateTime): WindowEnd {
|
|
if (resetsAt == null) return WindowEnd.NotRunning
|
|
return try {
|
|
WindowEnd.Ends(Duration.between(now, OffsetDateTime.parse(resetsAt)))
|
|
} catch (_: Exception) {
|
|
WindowEnd.Unreadable
|
|
}
|
|
}
|