Skip to content

Commit dd0b4fd

Browse files
committed
add Relative Time display mode in Transport Timeline
1 parent 197b0a8 commit dd0b4fd

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/model/UserResponse.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import com.sunnychung.application.multiplatform.hellohttp.annotation.Persisted
44
import com.sunnychung.application.multiplatform.hellohttp.document.Identifiable
55
import com.sunnychung.application.multiplatform.hellohttp.extension.endWithNewLine
66
import com.sunnychung.application.multiplatform.hellohttp.util.uuidString
7-
import com.sunnychung.lib.multiplatform.kdatetime.KInstant
7+
import com.sunnychung.lib.multiplatform.kdatetime.KDateTimeFormattable
88
import com.sunnychung.lib.multiplatform.kdatetime.KZoneOffset
9+
import com.sunnychung.lib.multiplatform.kdatetime.KZonedInstant
910
import com.sunnychung.lib.multiplatform.kdatetime.serializer.KInstantAsLong
1011
import kotlinx.serialization.Serializable
1112
import kotlinx.serialization.Transient
@@ -237,21 +238,32 @@ Duration: ${String.format("%.3f", (endAt!! - startAt!!).millis / 1000.0)}s
237238
else -> throw UnsupportedOperationException()
238239
}
239240

240-
fun UserResponse.describeTransportLayer() = buildString {
241+
fun UserResponse.describeTransportLayer(isRelativeTimeDisplay: Boolean) = buildString {
241242
val events = synchronized(rawExchange.exchanges) {
242243
rawExchange.exchanges.toList()
243244
}
245+
val startInstant = events.firstOrNull()?.instant ?: run {
246+
appendLine("No transportation")
247+
return@buildString
248+
}
244249
val titles = listOf(
245250
"Time",
246251
"Dir",
247252
"Stream",
248253
"Detail"
249254
)
255+
fun KZonedInstant.formatAbsTimeOrRelativeTime(): String =
256+
if (isRelativeTimeDisplay) {
257+
(this - startInstant).format("HH:mm:ss.lll")
258+
} else {
259+
this.format(TIME_FORMAT)
260+
}
261+
250262
val exportedData = events.map {
251263
listOf(
252-
it.instant.atZoneOffset(KZoneOffset.local()).format(TIME_FORMAT) +
264+
it.instant.atZoneOffset(KZoneOffset.local()).formatAbsTimeOrRelativeTime() +
253265
if (it.lastUpdateInstant != null && it.lastUpdateInstant != it.instant) {
254-
" ~ " + it.lastUpdateInstant!!.atZoneOffset(KZoneOffset.local()).format(TIME_FORMAT)
266+
" ~ " + it.lastUpdateInstant!!.atZoneOffset(KZoneOffset.local()).formatAbsTimeOrRelativeTime()
255267
} else {
256268
""
257269
},

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/TransportTimelineView.kt

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.sunnychung.application.multiplatform.hellohttp.ux
22

33
import androidx.compose.foundation.VerticalScrollbar
44
import androidx.compose.foundation.background
5+
import androidx.compose.foundation.clickable
56
import androidx.compose.foundation.layout.Box
67
import androidx.compose.foundation.layout.Column
78
import androidx.compose.foundation.layout.ColumnScope
@@ -70,6 +71,8 @@ fun TransportTimelineView(modifier: Modifier = Modifier, protocol: ProtocolVersi
7071
val density = LocalDensity.current
7172
val clipboardManager = LocalClipboardManager.current
7273

74+
var isRelativeTimeDisplay by remember { mutableStateOf(false) }
75+
7376
log.d { "TransportTimelineView recompose" }
7477

7578
val streamDigits = if (protocol?.isHttp2() == true) {
@@ -205,12 +208,23 @@ fun TransportTimelineView(modifier: Modifier = Modifier, protocol: ProtocolVersi
205208
// --- for copy button end
206209

207210
Column(modifier = modifier) {
208-
Box(modifier = Modifier.fillMaxWidth()) {
211+
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
212+
AppCheckbox(
213+
checked = isRelativeTimeDisplay,
214+
onCheckedChange = { isRelativeTimeDisplay = it },
215+
size = 24.dp,
216+
modifier = Modifier.padding(end = 4.dp)
217+
)
218+
AppText(
219+
text = "Relative Time Display",
220+
modifier = Modifier.clickable { isRelativeTimeDisplay = !isRelativeTimeDisplay },
221+
)
222+
Spacer(modifier = Modifier.weight(1f))
209223
AppTextButton(
210224
text = "Copy All",
211-
modifier = Modifier.align(Alignment.CenterEnd).padding(vertical = 4.dp, horizontal = 8.dp),
225+
modifier = Modifier.padding(vertical = 4.dp),
212226
) {
213-
val textToCopy = response.describeTransportLayer()
227+
val textToCopy = response.describeTransportLayer(isRelativeTimeDisplay = isRelativeTimeDisplay)
214228
clipboardManager.setText(AnnotatedString(textToCopy))
215229
AppContext.ErrorMessagePromptViewModel.showSuccessMessage("Copied text")
216230
}
@@ -297,6 +311,7 @@ fun TransportTimelineView(modifier: Modifier = Modifier, protocol: ProtocolVersi
297311
numCharsInALine = numCharsInALine,
298312
protocol = protocol,
299313
streamDigits = streamDigits,
314+
isRelativeTimeDisplay = isRelativeTimeDisplay,
300315
onMeasureContentWidth = { contentWidthInPx = it },
301316
onMeasureContentLines = { totalNumLines = it.totalNumLines },
302317
onPrepareComposable = {},
@@ -328,6 +343,7 @@ fun TransportTimelineView(modifier: Modifier = Modifier, protocol: ProtocolVersi
328343
numCharsInALine = numCharsInALine,
329344
protocol = protocol,
330345
streamDigits = streamDigits,
346+
isRelativeTimeDisplay = isRelativeTimeDisplay,
331347
onMeasureContentWidth = { contentWidthInPx = it },
332348
onMeasureContentLines = { totalNumLines = it.totalNumLines },
333349
onPrepareComposable = { composables += it },
@@ -372,6 +388,7 @@ private fun TransportTimelineContentView(
372388
numCharsInALine: Int,
373389
protocol: ProtocolVersion?,
374390
streamDigits: Int,
391+
isRelativeTimeDisplay: Boolean,
375392
onMeasureContentWidth: (Int) -> Unit,
376393
onMeasureContentLines: (TransportTimelineContentMeasureResult) -> Unit,
377394
onPrepareComposable: (@Composable () -> Unit) -> Unit,
@@ -434,6 +451,8 @@ private fun TransportTimelineContentView(
434451
TimestampColumn(
435452
createTime = it.instant,
436453
lastUpdateTime = it.lastUpdateInstant,
454+
isRelativeTimeDisplay = isRelativeTimeDisplay,
455+
startInstant = exchange.exchanges.first().instant,
437456
modifier = Modifier.width(TIMESTAMP_COLUMN_WIDTH_DP)
438457
.padding(end = 1.dp)
439458
)
@@ -545,10 +564,17 @@ fun lazyOrNormalItem(
545564
}
546565

547566
@Composable
548-
fun TimestampColumn(modifier: Modifier = Modifier, createTime: KInstant, lastUpdateTime: KInstant?) {
549-
var text = DATE_TIME_FORMAT.format(createTime.atZoneOffset(KZoneOffset.local()))
567+
fun TimestampColumn(modifier: Modifier = Modifier, createTime: KInstant, lastUpdateTime: KInstant?, isRelativeTimeDisplay: Boolean, startInstant: KInstant) {
568+
var text = DATE_TIME_FORMAT.format(
569+
createTime.atZoneOffset(KZoneOffset.local()).let { if (isRelativeTimeDisplay) it - startInstant else it }
570+
)
550571
if (lastUpdateTime != null && lastUpdateTime != createTime) {
551-
text = "$text ~ ${DATE_TIME_FORMAT.format(lastUpdateTime.atZoneOffset(KZoneOffset.local()))}"
572+
text = "$text ~ ${
573+
DATE_TIME_FORMAT.format(
574+
lastUpdateTime.atZoneOffset(KZoneOffset.local())
575+
.let { if (isRelativeTimeDisplay) it - startInstant else it }
576+
)
577+
}"
552578
}
553579

554580
// sometimes copy button is not working, due to Compose bug:

0 commit comments

Comments
 (0)