fix(hls): stick the upload counter + flip past tense between uploads

Two related UX issues on the HLS publish progress screen:

1. The Uploading PhaseRow's label used a fallback of
   `stringResource(hls_state_uploading_format, 0, 0)` whenever state
   wasn't Uploading, so the row flickered back to "Uploading 0 of 0"
   during the Transcoding phase between rendition uploads. The counter
   appeared to reset mid-flow even though every upload was succeeding.

2. With the counter now persisted, it still read "Uploading 2 of 5"
   while we were actually transcoding the next rendition — the count
   was right but the verb tense implied an upload was in flight when
   none was.

Persist lastDone + lastTotal in the composable via remember so the
counter reads monotonically across state transitions, and split the
label into two semantic variants:

- "Uploading %s (%d of %d)" — present tense, only when an upload is
  actually in flight (state is HlsPublishState.Uploading). The %d is
  the pre-incremented in-flight index.
- "Uploaded %d of %d" — past tense, used both in the gap between
  uploads (transcoding next rendition) and in the done/checkmark
  state. The %d is the last observed in-flight index, which after the
  lambda returns corresponds to the count that has actually finished.
- "Upload" — only the pre-first-upload idle state.

Also add a progressFraction that falls back to lastDone/lastTotal so
the progress bar under the row sticks too, matching the label.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
davotoula
2026-04-15 10:48:39 +02:00
parent 83d0dd9067
commit 0a649dbea6
2 changed files with 43 additions and 16 deletions
@@ -67,7 +67,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -508,27 +510,50 @@ private fun ProgressBody(
HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp))
val uploadingFraction =
(state as? HlsPublishState.Uploading)?.let { up ->
if (up.total == 0) 0f else up.done.toFloat() / up.total
// Persist the last-observed upload count across transitions so the Uploading row
// doesn't flicker back to "0 of 0" whenever state flips to Transcoding (between
// renditions) or Publishing (after the last upload). The counter should read
// monotonically: 1 of N → 2 of N → … → N of N as uploads actually complete.
var lastDone by remember { mutableIntStateOf(0) }
var lastTotal by remember { mutableIntStateOf(0) }
LaunchedEffect(state) {
if (state is HlsPublishState.Uploading) {
lastDone = state.done
lastTotal = state.total
}
}
val uploadingFraction =
if (lastTotal > 0) lastDone.toFloat() / lastTotal else null
val uploadingLabel =
when (state) {
is HlsPublishState.Uploading -> {
if (state.currentLabel.isNotBlank()) {
stringResource(
R.string.hls_state_uploading_with_label_format,
state.currentLabel,
state.done,
state.total,
)
} else {
stringResource(R.string.hls_state_uploading_format, state.done, state.total)
}
when {
// Currently in flight: present-tense, file label in the line.
state is HlsPublishState.Uploading && state.currentLabel.isNotBlank() -> {
stringResource(
R.string.hls_state_uploading_with_label_format,
state.currentLabel,
state.done,
state.total,
)
}
// Currently in flight, no file label (unreachable in practice — orchestrator
// always sets a label — but kept for completeness).
state is HlsPublishState.Uploading -> {
stringResource(R.string.hls_state_uploading_format, state.done, state.total)
}
// Between uploads or after all uploads finish: past-tense, monotonic count.
// The last uploaded file's index sticks on screen while the transcoder
// works on the next rendition, and lands on "Uploaded N of N" when the row
// flips to its checkmark/done state.
lastTotal > 0 -> {
stringResource(R.string.hls_state_uploaded_format, lastDone, lastTotal)
}
// Nothing has started uploading yet (very beginning of publish flow).
else -> {
stringResource(R.string.hls_state_uploading_format, 0, 0)
stringResource(R.string.hls_state_uploading_idle)
}
}
PhaseRow(
+2
View File
@@ -2106,8 +2106,10 @@
<string name="hls_publish_button">Publish HD video</string>
<string name="hls_publishing_header_format">Publishing “%1$s”…</string>
<string name="hls_state_transcoding_format">Transcoding %1$s</string>
<string name="hls_state_uploading_idle">Upload</string>
<string name="hls_state_uploading_format">Uploading %1$d of %2$d</string>
<string name="hls_state_uploading_with_label_format">Uploading %1$s (%2$d of %3$d)</string>
<string name="hls_state_uploaded_format">Uploaded %1$d of %2$d</string>
<string name="hls_state_publishing">Publishing event…</string>
<string name="hls_state_success_title">Video published</string>
<string name="hls_state_success_body">Your HD video is live on Nostr.</string>