mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 11:48:24 +00:00
Merge remote-tracking branch 'origin/claude/kind-keller-itzy63'
Vitor's day-divider clock fix (89232618) landed in the same file the
bubble refactor restructured, and in the three functions it moved:
`DaySeparator`, `sameDayAs` and `localDayOf` now live in
CordnMessageRow.kt beside the row that draws them.
Carried his fix over rather than dropping either side: `rememberToday`
and TODAY_POLL_MS move to CordnMessageRow.kt with the separator that
reads them, `DaySeparator` keeps its new `today` parameter, and the
screen still passes one clock per room. The conflicting hunks were the
old flat-row code the refactor deleted, so our side won those.
One tidy-up: his `val today = rememberToday()` had been inserted
directly under the PinnedRibbon comment block, which left that comment
describing the wrong statement. Moved above it with its own line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012BfD4txdnsaPRXmNXbup9n
This commit is contained in:
+4
-1
@@ -237,6 +237,9 @@ private fun CordnGroupChat(
|
||||
},
|
||||
) { padding ->
|
||||
Column(Modifier.fillMaxSize().padding(padding)) {
|
||||
// One clock for every divider in the room, so they cannot disagree.
|
||||
val today = rememberToday()
|
||||
|
||||
// Pinned messages sit above the conversation rather than inside it.
|
||||
// A pin is a claim about a message's importance, not a message, and
|
||||
// leaving it only in place means the thing someone pinned scrolls
|
||||
@@ -330,7 +333,7 @@ private fun CordnGroupChat(
|
||||
// Drawn under the first message of each day, which in a
|
||||
// reversed list means comparing against the older row.
|
||||
if (!message.sameDayAs(older)) {
|
||||
DaySeparator(message.envelope.createdAt)
|
||||
DaySeparator(message.envelope.createdAt, today)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-2
@@ -39,6 +39,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -84,9 +85,12 @@ import com.vitorpamplona.quartz.cordn.spec02Envelopes.CordnAnnotationIndex
|
||||
import com.vitorpamplona.quartz.cordn.spec02Envelopes.CordnDeliveredMessage
|
||||
import com.vitorpamplona.quartz.cordn.spec02Envelopes.CordnMessageReferences
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import kotlinx.coroutines.delay
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.math.abs
|
||||
|
||||
@@ -532,9 +536,11 @@ private fun localDayOf(epochSeconds: Long): LocalDate = Instant.ofEpochSecond(ep
|
||||
* morning" sit flush against each other.
|
||||
*/
|
||||
@Composable
|
||||
internal fun DaySeparator(createdAt: Long) {
|
||||
internal fun DaySeparator(
|
||||
createdAt: Long,
|
||||
today: LocalDate,
|
||||
) {
|
||||
val day = remember(createdAt) { localDayOf(createdAt) }
|
||||
val today = remember { LocalDate.now(ZoneId.systemDefault()) }
|
||||
|
||||
val label =
|
||||
when (day) {
|
||||
@@ -564,3 +570,33 @@ internal fun DaySeparator(createdAt: Long) {
|
||||
HorizontalDivider(Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
|
||||
/** Upper bound on how long a stale "Today" can survive a clock correction. */
|
||||
private const val TODAY_POLL_MS = 60_000L
|
||||
|
||||
/**
|
||||
* Today, as a value that stops being today when it stops being today.
|
||||
*
|
||||
* [DaySeparator] used to hold `remember { LocalDate.now() }` of its own. That is a
|
||||
* snapshot of the wall clock with nothing to invalidate it, and each separator keeps a
|
||||
* separate one, so they can disagree: a separator composed before midnight goes on
|
||||
* saying "Today" while the one for the new day says it too. Seen on the tablet — one
|
||||
* room, two "Today" dividers.
|
||||
*
|
||||
* Polling rather than a single sleep to the next midnight, because a device clock does
|
||||
* not only advance: it is corrected, and the tablet this was found on jumped nine hours
|
||||
* in one step. Re-assigning an equal [LocalDate] is not a change, so a quiet minute
|
||||
* costs no recomposition.
|
||||
*/
|
||||
@Composable
|
||||
internal fun rememberToday(): LocalDate {
|
||||
val zone = remember { ZoneId.systemDefault() }
|
||||
return produceState(LocalDate.now(zone), zone) {
|
||||
while (true) {
|
||||
val now = ZonedDateTime.now(zone)
|
||||
val untilMidnight = Duration.between(now, now.toLocalDate().plusDays(1).atStartOfDay(zone)).toMillis()
|
||||
delay(untilMidnight.coerceIn(1_000L, TODAY_POLL_MS))
|
||||
value = LocalDate.now(zone)
|
||||
}
|
||||
}.value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user