Let the reader put the session list in its own order
Nothing sorts the sessions tab any more. The order is the server's `sessions` list, which is the reader's arrangement: holding a row puts the screen in selection mode -- the same gesture and the same bottom bar as the import tab -- and each card grows a burger handle at its right edge that drags the row to a new place, with a tick of haptic feedback for each one it passes. The two attempts this replaces, sorting by activity and then by when each agent was turned on, were both looking for an order a session could not move itself out of; no rule computed from what a session is doing can be one. `POST /sessions/order` rewrites the config's order, so it is the same on every device and survives a backend restart, and `SessionConfig::started` goes with the sort that needed it. Rearranging is independent of the selection: the handle moves the row it is on, picked out or not. The click moved off the card and onto its contents so that a press landing on the handle cannot also select the row it is about to move. Selection's one action is Delete, which now takes the whole set. Two traps in `Reorder.kt`, both measured on the emulator and written down in `this-machine-android`: a crossing is decided from how far the finger has travelled, because a lazy list animates an item into its new place and its `offset` reports the old one for several frames; and the viewport is pinned with `requestScrollToItem` around each move, because a lazy list keeps its place by the key of the top item and would otherwise follow the row being dragged. Verified on the emulator against the sandbox: the order survives an app restart and a backend read-back, a two-row drag moves exactly two rows, a drag to the bottom edge scrolls the list and lands the row last, pressing the handle without moving changes nothing, and deleting two selected sessions leaves the rest in place.
This commit is contained in:
1 parent
942edd6b31
commit
b7fd18b195
12 files changed
+801
-339
No files matched your search
@@ -1,87 +0,0 @@
|
||||
package com.example.aiapp
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class SessionOrderTest {
|
||||
@Test
|
||||
fun `running sessions keep the order their agents were turned on in`() {
|
||||
val first = session("first", status = "running", started = 10.0, lastActivity = 900.0)
|
||||
val second =
|
||||
session("second", status = "awaitingInput", started = 20.0, lastActivity = 20.0)
|
||||
val third = session("third", status = "waiting", started = 30.0, lastActivity = 500.0)
|
||||
|
||||
assertEquals(
|
||||
listOf("first", "second", "third"),
|
||||
sessionsInListOrder(listOf(third, second, first)).map { it.id },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a session started again joins the bottom of the running group`() {
|
||||
val old = session("old", status = "idle", started = 10.0, lastActivity = 10.0)
|
||||
val restarted = session("restarted", status = "idle", started = 99.0, lastActivity = 99.0)
|
||||
|
||||
assertEquals(
|
||||
listOf("old", "restarted"),
|
||||
sessionsInListOrder(listOf(restarted, old)).map { it.id },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `stopped sessions come after the running ones, most recent first`() {
|
||||
val running = session("running", status = "idle", started = 100.0, lastActivity = 100.0)
|
||||
val stale = session("stale", status = "exited", started = 1.0, lastActivity = 5.0)
|
||||
val recent = session("recent", status = "exited", started = 2.0, lastActivity = 50.0)
|
||||
|
||||
assertEquals(
|
||||
listOf("running", "recent", "stale"),
|
||||
sessionsInListOrder(listOf(stale, recent, running)).map { it.id },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A session whose process nobody could ask about still has one, so it stays where it was rather
|
||||
* than dropping into the stopped group the moment a machine goes quiet.
|
||||
*/
|
||||
@Test
|
||||
fun `a session of unknown state is one of the running ones`() {
|
||||
val unknown = session("unknown", status = "unknown", started = 10.0, lastActivity = 10.0)
|
||||
val stopped = session("stopped", status = "exited", started = 5.0, lastActivity = 999.0)
|
||||
|
||||
assertEquals(
|
||||
listOf("unknown", "stopped"),
|
||||
sessionsInListOrder(listOf(stopped, unknown)).map { it.id },
|
||||
)
|
||||
}
|
||||
|
||||
private fun session(id: String, status: String, started: Double, lastActivity: Double) =
|
||||
SessionSummary(
|
||||
id = id,
|
||||
machine = "machine",
|
||||
machineName = "machine",
|
||||
provider = "echo",
|
||||
title = id,
|
||||
model = null,
|
||||
keepsOwnTranscript = false,
|
||||
ownTranscriptName = null,
|
||||
permissionMode = null,
|
||||
effort = null,
|
||||
takesEffort = false,
|
||||
imported = false,
|
||||
notify = true,
|
||||
autoResume = false,
|
||||
autoResumeMessage = DEFAULT_RESUME_MESSAGE,
|
||||
resumeAt = null,
|
||||
cwd = null,
|
||||
contextTokens = null,
|
||||
contextLimit = null,
|
||||
params = emptyMap(),
|
||||
maxImageEdge = null,
|
||||
usageProvider = null,
|
||||
status = status,
|
||||
lastActivity = lastActivity,
|
||||
started = started,
|
||||
backgroundTasks = 0,
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user