feat(rust): a Rust server's next wipe (module-rust phase 16, M18)
All checks were successful
PR Checks / android-build (pull_request) Successful in 9m28s
All checks were successful
PR Checks / android-build (pull_request) Successful in 9m28s
RustServerDto gains nextWipe { at, source }, null both when the operator
set no schedule and against a module older than phase 16. The server
list row and the server screen's header read "Next wipe Thu 1 Oct,
19:00 · in 6 days" in the phone's own zone, and "(rescheduled)" for a
one-off date the operator set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -68,6 +68,24 @@ data class RustServerDto(
|
||||
/** When this module last wrote the row — a failed poll moves it too. */
|
||||
val updatedAt: String? = null,
|
||||
val stale: Boolean = false,
|
||||
/**
|
||||
* When the server wipes next (module-rust phase 16, D130), or null. Null
|
||||
* both when the operator set no schedule and against a module older than
|
||||
* phase 16, which does not send the field — either way nothing is drawn.
|
||||
*/
|
||||
val nextWipe: RustNextWipeDto? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* The next wipe, as the module computed it from the operator's schedule on this
|
||||
* read. [source] is `forced` (Facepunch's monthly forced wipe), `rule` (the
|
||||
* server's own weekly or biweekly day) or `once` (a one-off date the operator
|
||||
* set — a delayed or an extra wipe). An unknown word is shown like `rule`.
|
||||
*/
|
||||
@Serializable
|
||||
data class RustNextWipeDto(
|
||||
val at: String? = null,
|
||||
val source: String? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,29 @@ fun wipeDay(
|
||||
.format(at.atZone(zone))
|
||||
}
|
||||
|
||||
/**
|
||||
* The next wipe (module-rust phase 16), as `Thu 1 Oct, 19:00 · in 6 days` in
|
||||
* the PHONE's zone — or null when there is no instant to show.
|
||||
*
|
||||
* The module computes the instant from the operator's rule in the operator's
|
||||
* zone; the phone only says it the reader's way, which is the same split the
|
||||
* website's `nextWipe()` makes. The `(rescheduled)` mark for a one-off date is
|
||||
* the caller's, from a string resource.
|
||||
*/
|
||||
fun nextWipeWhen(
|
||||
value: String?,
|
||||
now: Instant = Instant.now(),
|
||||
zone: ZoneId = ZoneId.systemDefault(),
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String? {
|
||||
val at = parseWireInstant(value) ?: return null
|
||||
val local = at.atZone(zone)
|
||||
val date = DateTimeFormatter.ofPattern("EEE d MMM", locale).format(local)
|
||||
val time = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withLocale(locale).format(local)
|
||||
val distance = rustAgo(value, now) ?: return "$date, $time"
|
||||
return "$date, $time · $distance"
|
||||
}
|
||||
|
||||
/**
|
||||
* "3 minutes ago", for a "last reported" line.
|
||||
*
|
||||
|
||||
@@ -186,6 +186,15 @@ private fun ServerHeader(
|
||||
)
|
||||
}
|
||||
|
||||
nextWipeLine(server)?.let {
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 2.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
|
||||
@@ -142,6 +142,15 @@ private fun ServerRow(server: RustServerDto, onOpen: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
nextWipeLine(server)?.let {
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 2.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = lastReported(server),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
@@ -170,6 +179,22 @@ internal fun describeWorld(server: RustServerDto): String? {
|
||||
return parts.takeIf { it.isNotEmpty() }?.joinToString(" · ")
|
||||
}
|
||||
|
||||
/**
|
||||
* "Next wipe Thu 1 Oct, 19:00 · in 6 days", with "(rescheduled)" for a one-off
|
||||
* date — or null, and the caller draws no line, when the server has no schedule
|
||||
* or the module predates phase 16 and sends no field.
|
||||
*/
|
||||
@Composable
|
||||
internal fun nextWipeLine(server: RustServerDto): String? {
|
||||
val next = server.nextWipe ?: return null
|
||||
val whenText = nextWipeWhen(next.at) ?: return null
|
||||
return if (next.source == "once") {
|
||||
stringResource(R.string.rust_next_wipe_rescheduled, whenText)
|
||||
} else {
|
||||
stringResource(R.string.rust_next_wipe, whenText)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "last reported 3 minutes ago".
|
||||
*
|
||||
|
||||
@@ -578,6 +578,8 @@
|
||||
<string name="rust_world_size">size %1$d</string>
|
||||
<string name="rust_world_seed">seed %1$d</string>
|
||||
<string name="rust_wiped_on">wiped %1$s</string>
|
||||
<string name="rust_next_wipe">Next wipe %1$s</string>
|
||||
<string name="rust_next_wipe_rescheduled">Next wipe %1$s (rescheduled)</string>
|
||||
<string name="rust_refresh_failed">Could not refresh just now. This is the last thing the site heard.</string>
|
||||
<string name="rust_no_such_server">No such server</string>
|
||||
<string name="rust_no_such_server_detail">This address does not name a server this site follows.</string>
|
||||
|
||||
Reference in New Issue
Block a user