Skip to content

Commit da17cba

Browse files
committed
refactor: migrate time elapsed input to TextFieldState with transformations
1 parent 2041004 commit da17cba

1 file changed

Lines changed: 44 additions & 13 deletions

File tree

app/src/main/java/org/librefit/ui/screens/beforeSaving/BeforeSavingScreen.kt

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import androidx.compose.foundation.layout.Row
2121
import androidx.compose.foundation.layout.fillMaxWidth
2222
import androidx.compose.foundation.layout.padding
2323
import 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
2427
import androidx.compose.material3.DatePicker
2528
import androidx.compose.material3.DatePickerDialog
2629
import androidx.compose.material3.ElevatedCard
@@ -35,10 +38,12 @@ import androidx.compose.material3.Text
3538
import androidx.compose.material3.TextButton
3639
import androidx.compose.material3.rememberDatePickerState
3740
import androidx.compose.runtime.Composable
41+
import androidx.compose.runtime.LaunchedEffect
3842
import androidx.compose.runtime.getValue
3943
import androidx.compose.runtime.mutableStateOf
4044
import androidx.compose.runtime.remember
4145
import androidx.compose.runtime.setValue
46+
import androidx.compose.runtime.snapshotFlow
4247
import androidx.compose.ui.Alignment
4348
import androidx.compose.ui.Modifier
4449
import androidx.compose.ui.draw.clip
@@ -54,6 +59,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
5459
import androidx.navigation.NavHostController
5560
import androidx.navigation.compose.rememberNavController
5661
import kotlinx.collections.immutable.persistentListOf
62+
import kotlinx.coroutines.flow.collectLatest
5763
import org.librefit.R
5864
import org.librefit.enums.InfoMode
5965
import org.librefit.enums.SetMode
@@ -76,7 +82,8 @@ import org.librefit.ui.models.UiSet
7682
import org.librefit.ui.models.UiWorkout
7783
import org.librefit.ui.theme.LibreFitTheme
7884
import 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
8087
import 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

Comments
 (0)