diff --git a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/durationeditor/WorkoutDurationEditorDialog.kt b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/durationeditor/WorkoutDurationEditorDialog.kt index 34dc17e5..75846dfb 100644 --- a/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/durationeditor/WorkoutDurationEditorDialog.kt +++ b/modules/common-ui-components-workout-editor/src/main/java/com/ankitsuda/rebound/ui/components/workouteditor/durationeditor/WorkoutDurationEditorDialog.kt @@ -21,11 +21,16 @@ import androidx.compose.material.TextButton import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog -import com.ankitsuda.base.utils.toString +import androidx.compose.ui.window.DialogProperties +import com.ankitsuda.base.utils.toDurationStr +import com.ankitsuda.base.utils.toEpochMillis +import com.ankitsuda.rebound.ui.components.BasicInfoCard +import com.ankitsuda.rebound.ui.components.BasicInfoCardType import com.ankitsuda.rebound.ui.components.RButtonStyle2 import com.ankitsuda.rebound.ui.components.datetimepicker.DateTimePicker import com.ankitsuda.rebound.ui.components.workouteditor.R @@ -34,6 +39,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.time.format.FormatStyle +@OptIn(ExperimentalComposeUiApi::class) @Composable fun WorkoutDurationEditorDialog( initialStartDateTime: LocalDateTime, @@ -77,8 +83,20 @@ fun WorkoutDurationEditorDialog( getFormattedDateString(endDateTime) } + val durationStr = remember(startDateTime, endDateTime) { + (endDateTime.toEpochMillis() - startDateTime.toEpochMillis()).toDurationStr() + } + + val errorMessage = if (startDateTime > endDateTime) { + stringResource(id = R.string.duration_error_start_longer_than_end) + } else { + null + } + + Dialog( onDismissRequest = onDismissRequest, + properties = DialogProperties(usePlatformDefaultWidth = false) ) { Surface( modifier = Modifier @@ -90,53 +108,79 @@ fun WorkoutDurationEditorDialog( Column( modifier = Modifier .fillMaxWidth() +// .verticalScroll(state = rememberScrollState()) + .padding( + start = 16.dp, + end = 16.dp, + top = 16.dp, + bottom = 8.dp + ), + verticalArrangement = Arrangement.spacedBy(12.dp) ) { - Column( - modifier = - Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = stringResource(R.string.adjust_duration), - style = ReboundTheme.typography.h6, - color = ReboundTheme.colors.onBackground - ) + Text( + text = stringResource(R.string.adjust_duration), + style = ReboundTheme.typography.h6, + color = ReboundTheme.colors.onBackground + ) + if (durationStr.isNotBlank()) { Text( - text = stringResource(R.string.start_time), + text = stringResource(R.string.duration), style = ReboundTheme.typography.caption, color = ReboundTheme.colors.onBackground.copy(0.75f) ) - RButtonStyle2( - modifier = Modifier.fillMaxWidth(), - text = formattedStartDateTime, - onClick = { - currentPickerActiveFor = 1 - } - ) - Text( - text = stringResource(R.string.end_time), - style = ReboundTheme.typography.caption, - color = ReboundTheme.colors.onBackground.copy(0.75f) + text = durationStr, + style = ReboundTheme.typography.body1, + color = ReboundTheme.colors.onBackground ) + } - RButtonStyle2( + Text( + text = stringResource(R.string.start_time), + style = ReboundTheme.typography.caption, + color = ReboundTheme.colors.onBackground.copy(0.75f) + ) + + RButtonStyle2( + modifier = Modifier.fillMaxWidth(), + text = formattedStartDateTime, + onClick = { + currentPickerActiveFor = 1 + } + ) + + Text( + text = stringResource(R.string.end_time), + style = ReboundTheme.typography.caption, + color = ReboundTheme.colors.onBackground.copy(0.75f) + ) + + RButtonStyle2( + modifier = Modifier.fillMaxWidth(), + text = formattedEndDateTime, + onClick = { + currentPickerActiveFor = 2 + } + ) + + errorMessage?.let { + BasicInfoCard( modifier = Modifier.fillMaxWidth(), - text = formattedEndDateTime, - onClick = { - currentPickerActiveFor = 2 - } - ) - - DialogButtonsRow( - onClickSave = ::handleSaveClick, - onClickCancel = onDismissRequest + type = BasicInfoCardType.ERROR, + message = it ) } + + DialogButtonsRow( + isSaveEnabled = errorMessage == null, + onClickSave = ::handleSaveClick, + onClickCancel = onDismissRequest + ) } } + } if (currentPickerActiveFor == 1 || currentPickerActiveFor == 2) { @@ -161,6 +205,7 @@ fun WorkoutDurationEditorDialog( @Composable private fun ColumnScope.DialogButtonsRow( + isSaveEnabled: Boolean = true, onClickSave: () -> Unit, onClickCancel: () -> Unit ) { @@ -172,7 +217,7 @@ private fun ColumnScope.DialogButtonsRow( TextButton(onClick = onClickCancel) { Text(stringResource(id = R.string.cancel)) } - TextButton(onClick = onClickSave) { + TextButton(enabled = isSaveEnabled, onClick = onClickSave) { Text(stringResource(id = R.string.save)) } } diff --git a/modules/common-ui-components/src/main/java/com/ankitsuda/rebound/ui/components/BasicInfoCard.kt b/modules/common-ui-components/src/main/java/com/ankitsuda/rebound/ui/components/BasicInfoCard.kt new file mode 100644 index 00000000..06664161 --- /dev/null +++ b/modules/common-ui-components/src/main/java/com/ankitsuda/rebound/ui/components/BasicInfoCard.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Ankit Suda. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package com.ankitsuda.rebound.ui.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Done +import androidx.compose.material.icons.outlined.Error +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material.icons.outlined.Warning +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import com.ankitsuda.rebound.ui.theme.ReboundTheme + +enum class BasicInfoCardType { + INFO, + WARNING, + ERROR, + SUCCESS; +} + +@Composable +fun BasicInfoCard( + modifier: Modifier = Modifier, + type: BasicInfoCardType = BasicInfoCardType.INFO, + iconEnabled: Boolean = true, + customIcon: ImageVector? = null, + title: String? = null, + message: String, +) { + val icon = customIcon ?: when (type) { + BasicInfoCardType.INFO -> Icons.Outlined.Info + BasicInfoCardType.WARNING -> Icons.Outlined.Warning + BasicInfoCardType.ERROR -> Icons.Outlined.Error + BasicInfoCardType.SUCCESS -> Icons.Outlined.Done + } + + AppCard( + modifier = modifier, +// backgroundColor = cardColor, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (iconEnabled) { + Icon( + imageVector = icon, contentDescription = null + ) + } + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + title?.let { + Text( + text = it, + style = ReboundTheme.typography.body1.copy(color = ReboundTheme.colors.onBackground) + ) + } + Text( + text = message, + style = ReboundTheme.typography.body2.copy( + color = ReboundTheme.colors.onBackground.copy( + alpha = 0.75f + ) + ) + ) + } + } + } +} \ No newline at end of file diff --git a/modules/common-ui-resources/src/main/res/values/strings.xml b/modules/common-ui-resources/src/main/res/values/strings.xml index 4b66fb4c..e15fd1ce 100644 --- a/modules/common-ui-resources/src/main/res/values/strings.xml +++ b/modules/common-ui-resources/src/main/res/values/strings.xml @@ -251,4 +251,5 @@ Adjust duration Start time End time + Start time is longer than end time \ No newline at end of file