@@ -21,6 +21,9 @@ import androidx.compose.foundation.layout.Row
2121import androidx.compose.foundation.layout.fillMaxWidth
2222import androidx.compose.foundation.layout.padding
2323import androidx.compose.foundation.text.KeyboardOptions
24+ import androidx.compose.foundation.text.input.TextFieldLineLimits
25+ import androidx.compose.foundation.text.input.rememberTextFieldState
26+ import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
2427import androidx.compose.material3.DatePicker
2528import androidx.compose.material3.DatePickerDialog
2629import androidx.compose.material3.ElevatedCard
@@ -35,10 +38,12 @@ import androidx.compose.material3.Text
3538import androidx.compose.material3.TextButton
3639import androidx.compose.material3.rememberDatePickerState
3740import androidx.compose.runtime.Composable
41+ import androidx.compose.runtime.LaunchedEffect
3842import androidx.compose.runtime.getValue
3943import androidx.compose.runtime.mutableStateOf
4044import androidx.compose.runtime.remember
4145import androidx.compose.runtime.setValue
46+ import androidx.compose.runtime.snapshotFlow
4247import androidx.compose.ui.Alignment
4348import androidx.compose.ui.Modifier
4449import androidx.compose.ui.draw.clip
@@ -54,6 +59,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
5459import androidx.navigation.NavHostController
5560import androidx.navigation.compose.rememberNavController
5661import kotlinx.collections.immutable.persistentListOf
62+ import kotlinx.coroutines.flow.collectLatest
5763import org.librefit.R
5864import org.librefit.enums.InfoMode
5965import org.librefit.enums.SetMode
@@ -76,7 +82,8 @@ import org.librefit.ui.models.UiSet
7682import org.librefit.ui.models.UiWorkout
7783import org.librefit.ui.theme.LibreFitTheme
7884import org.librefit.util.Formatter
79- import org.librefit.util.Formatter.formatTime
85+ import org.librefit.util.textFieldTransformations.TimeInputTransformation
86+ import org.librefit.util.textFieldTransformations.TimeOutputTransformation
8087import kotlin.time.Duration.Companion.seconds
8188
8289@OptIn(ExperimentalMaterial3Api ::class , ExperimentalSharedTransitionApi ::class )
@@ -202,6 +209,38 @@ fun SharedTransitionScope.BeforeSavingScreenContent(
202209 setTimeElapsed : (Int ) -> Unit ,
203210 onInputModalBottomSheetRequest : (InputModalBottomSheetState ) -> Unit ,
204211) {
212+ val timeTextFieldState = rememberTextFieldState(
213+ initialText = Formatter .formatTime(workout.timeElapsed).filter { it != ' :' }
214+ )
215+ val inputTransformation = remember { TimeInputTransformation (true ) }
216+ val outputTransformation = remember { TimeOutputTransformation (true ) }
217+
218+ // Sync elapsed time with time text field
219+ LaunchedEffect (workout.timeElapsed) {
220+ val formatted =
221+ Formatter .formatTime(workout.timeElapsed).filter { it != ' :' }
222+ if (timeTextFieldState.text.toString() != formatted) {
223+ // Update ONLY when state differs and user apply previous set but when he edited with the keyboard
224+ timeTextFieldState.setTextAndPlaceCursorAtEnd(formatted)
225+ }
226+ }
227+
228+ // Sync time text field with elapsed time
229+ LaunchedEffect (timeTextFieldState) {
230+ snapshotFlow { timeTextFieldState.text.toString() }.collectLatest { rawText ->
231+ val padded = rawText.padStart(6 , ' 0' )
232+ val seconds = padded.takeLast(2 )
233+ val minutes = padded.dropLast(2 ).takeLast(2 )
234+ val hours = padded.dropLast(4 ).takeLast(2 )
235+ val newValue = Formatter .parseTimeInputToSeconds(
236+ input = " $hours :$minutes :$seconds "
237+ )
238+ if (newValue != workout.timeElapsed) {
239+ setTimeElapsed(newValue)
240+ }
241+ }
242+ }
243+
205244 LibreFitScaffold (
206245 title = AnnotatedString (stringResource(R .string.overview)),
207246 navigateBack = navController::navigateUp,
@@ -270,19 +309,11 @@ fun SharedTransitionScope.BeforeSavingScreenContent(
270309 OutlinedTextField (
271310 readOnly = useScrollWheelForInput,
272311 shape = MaterialTheme .shapes.large,
273- value = formatTime(workout.timeElapsed) ,
312+ state = timeTextFieldState ,
274313 label = { Text (stringResource(R .string.elapsed_time)) },
275- onValueChange = { string ->
276- val stringValue = string.filter { it.isDigit() }.takeLast(6 )
277-
278- val seconds = stringValue.toInt() % 100
279- val minutes = (stringValue.toInt() % 10000 - seconds) / 100
280- val hours =
281- (stringValue.toInt() - stringValue.toInt() % 10000 ) / 10000
282-
283- setTimeElapsed(hours * 3600 + minutes * 60 + seconds)
284- },
285- singleLine = true ,
314+ lineLimits = TextFieldLineLimits .SingleLine ,
315+ inputTransformation = inputTransformation,
316+ outputTransformation = outputTransformation,
286317 keyboardOptions = KeyboardOptions (keyboardType = KeyboardType .Number )
287318 )
288319 if (useScrollWheelForInput) {
0 commit comments