From 0a649dbea6f46b51cfd98d8fe1b5ca3824ec76bc Mon Sep 17 00:00:00 2001 From: davotoula Date: Wed, 15 Apr 2026 10:48:39 +0200 Subject: [PATCH] fix(hls): stick the upload counter + flip past tense between uploads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../loggedIn/video/hls/NewHlsVideoScreen.kt | 57 +++++++++++++------ amethyst/src/main/res/values/strings.xml | 2 + 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt index 28d30bb5c..316aeaf62 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/hls/NewHlsVideoScreen.kt @@ -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( diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 8f26c16bf..07ecc77b2 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -2106,8 +2106,10 @@ Publish HD video Publishing “%1$s”… Transcoding %1$s + Upload Uploading %1$d of %2$d Uploading %1$s (%2$d of %3$d) + Uploaded %1$d of %2$d Publishing event… Video published Your HD video is live on Nostr.