Merge pull request #111 from SELab1/delete_timer

Delete timer
This commit is contained in:
lbarraga 2023-05-15 21:43:23 +02:00 committed by GitHub Enterprise
commit 3386d45a04
7 changed files with 121 additions and 41 deletions

View file

@ -3,7 +3,6 @@ package be.ugent.sel.studeez.common.composable
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
@ -22,7 +21,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources
import kotlin.math.sin
import be.ugent.sel.studeez.R.drawable as AppIcon
import be.ugent.sel.studeez.R.string as AppText
@ -47,7 +45,7 @@ fun LabelledInputField(
value: String,
onNewValue: (String) -> Unit,
@StringRes label: Int,
singleLine: Boolean = false
singleLine: Boolean = true
) {
OutlinedTextField(
value = value,
@ -119,7 +117,9 @@ fun LabeledErrorTextField(
initialValue: String,
@StringRes label: Int,
singleLine: Boolean = false,
errorText: Int,
isValid: MutableState<Boolean> = remember { mutableStateOf(true) },
isFirst: MutableState<Boolean> = remember { mutableStateOf(false) },
@StringRes errorText: Int,
keyboardType: KeyboardType,
predicate: (String) -> Boolean,
onNewCorrectValue: (String) -> Unit
@ -128,31 +128,28 @@ fun LabeledErrorTextField(
mutableStateOf(initialValue)
}
var isValid by remember {
mutableStateOf(predicate(value))
}
Column {
OutlinedTextField(
modifier = modifier.fieldModifier(),
value = value,
onValueChange = { newText ->
isFirst.value = false
value = newText
isValid = predicate(value)
if (isValid) {
isValid.value = predicate(value)
if (isValid.value) {
onNewCorrectValue(newText)
}
},
singleLine = singleLine,
label = { Text(text = stringResource(id = label)) },
isError = !isValid,
isError = !isValid.value && !isFirst.value,
keyboardOptions = KeyboardOptions(
keyboardType = keyboardType,
imeAction = ImeAction.Done
)
)
if (!isValid) {
if (!isValid.value && !isFirst.value) {
Text(
modifier = Modifier.padding(start = 16.dp),
text = stringResource(id = errorText),

View file

@ -3,6 +3,8 @@ package be.ugent.sel.studeez.screens.timer_form
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import be.ugent.sel.studeez.common.composable.DeleteButton
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.common.composable.FormComposable
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.R.string as AppText
@ -12,8 +14,16 @@ fun TimerAddRoute(
popUp: () -> Unit,
viewModel: TimerFormViewModel
) {
TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.add_timer) {
viewModel.saveTimer(it, goBack = popUp)
TimerFormScreen(
popUp = popUp,
getTimerInfo = viewModel::getTimerInfo,
extraButton= { },
AppText.add_timer
) {
viewModel.saveTimer(it, goBack = {popUp(); popUp()})
}
}
@ -22,7 +32,20 @@ fun TimerEditRoute(
popUp: () -> Unit,
viewModel: TimerFormViewModel
) {
TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.edit_timer) {
@Composable
fun deleteButton() {
DeleteButton(text = AppText.delete_timer) {
viewModel.deleteTimer(viewModel.getTimerInfo(), popUp)
}
}
TimerFormScreen(
popUp = popUp,
getTimerInfo = viewModel::getTimerInfo,
extraButton= { deleteButton() },
AppText.edit_timer
) {
viewModel.editTimer(it, goBack = popUp)
}
}
@ -31,6 +54,7 @@ fun TimerEditRoute(
fun TimerFormScreen(
popUp: () -> Unit,
getTimerInfo: () -> TimerInfo,
extraButton: @Composable () -> Unit,
@StringRes label: Int,
onConfirmClick: (TimerInfo) -> Unit
) {
@ -40,6 +64,6 @@ fun TimerFormScreen(
title = stringResource(id = label),
popUp = popUp
) {
timerFormScreen(onConfirmClick)
timerFormScreen(onConfirmClick, extraButton)
}
}

View file

@ -23,6 +23,11 @@ class TimerFormViewModel @Inject constructor(
goBack()
}
fun deleteTimer(timerInfo: TimerInfo, goBack: () -> Unit) {
timerDAO.deleteTimer(timerInfo)
goBack()
}
fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) {
timerDAO.saveTimer(timerInfo)
goBack()

View file

@ -3,49 +3,82 @@ package be.ugent.sel.studeez.screens.timer_form.form_screens
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.LabelledInputField
import be.ugent.sel.studeez.common.composable.LabeledErrorTextField
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.snackbar.SnackbarManager
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.R.string as AppText
abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) {
protected val valids = mutableMapOf(
"name" to mutableStateOf(textPredicate(timerInfo.name)),
"description" to mutableStateOf(textPredicate(timerInfo.description))
)
protected val firsts = mutableMapOf(
"name" to mutableStateOf(true),
"description" to mutableStateOf(true)
)
@Composable
operator fun invoke(onSaveClick: (TimerInfo) -> Unit) {
var name by remember { mutableStateOf(timerInfo.name) }
var description by remember { mutableStateOf(timerInfo.description) }
// This shall rerun whenever name and description change
timerInfo.name = name
timerInfo.description = description
operator fun invoke(
onSaveClick: (TimerInfo) -> Unit,
extraButton: @Composable () -> Unit = {},
) {
Column {
// Fields that every timer shares (ommited id)
LabelledInputField(
value = name,
onNewValue = { name = it },
label = R.string.name
)
LabeledErrorTextField(
initialValue = timerInfo.name,
label = R.string.name,
errorText = AppText.name_error,
isValid = valids.getValue("name"),
isFirst = firsts.getValue("name"),
keyboardType = KeyboardType.Text,
predicate = { it.isNotBlank() }
) { correctName ->
timerInfo.name = correctName
}
LabelledInputField(
value = description,
onNewValue = { description = it },
label = AppText.description,
singleLine = false
)
LabeledErrorTextField(
initialValue = timerInfo.description,
label = R.string.description,
errorText = AppText.description_error,
isValid = valids.getValue("description"),
isFirst = firsts.getValue("description"),
singleLine = false,
keyboardType = KeyboardType.Text,
predicate = { textPredicate(it) }
) { correctName ->
timerInfo.description = correctName
}
ExtraFields()
BasicButton(R.string.save, Modifier.basicButton()) {
onSaveClick(timerInfo)
if (valids.all { it.component2().value }) { // All fields are valid
onSaveClick(timerInfo)
} else {
firsts.map {
it.component2().value = false
} // dont mask error because its not been filled out yet
SnackbarManager.showMessage(AppText.fill_out_error)
}
}
extraButton()
}
}
private fun textPredicate(text: String): Boolean {
return text.isNotBlank()
}
@Composable
open fun ExtraFields() {
// By default no extra fields, unless overwritten by subclass.

View file

@ -15,6 +15,8 @@ class BreakTimerFormScreen(
private val breakTimerInfo: PomodoroTimerInfo
): AbstractTimerFormScreen(breakTimerInfo) {
@Composable
override fun ExtraFields() {
// If the user presses the OK button on the timepicker, the time in the button should change
@ -26,12 +28,17 @@ class BreakTimerFormScreen(
breakTimerInfo.breakTime = newTime
}
valids["repeats"] = remember {mutableStateOf(true)}
firsts["repeats"] = remember { mutableStateOf(true) }
LabeledErrorTextField(
initialValue = breakTimerInfo.repeats.toString(),
label = R.string.repeats,
errorText = AppText.repeats_error,
isValid = valids.getValue("repeats"),
isFirst = firsts.getValue("repeats"),
keyboardType = KeyboardType.Decimal,
predicate = { it.matches(Regex("[1-9]+\\d*")) }
predicate = { isNumber(it) }
) { correctlyTypedInt ->
breakTimerInfo.repeats = correctlyTypedInt.toInt()
}
@ -39,6 +46,10 @@ class BreakTimerFormScreen(
}
}
fun isNumber(text: String): Boolean {
return text.matches(Regex("[1-9]+\\d*"))
}
@Preview
@Composable
fun BreakEditScreenPreview() {

View file

@ -1,13 +1,13 @@
package be.ugent.sel.studeez.screens.timer_form.timer_type_select
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.data.local.models.timer_info.*
@ -37,7 +37,10 @@ fun TimerTypeSelectScreen(
) {
TimerType.values().forEach { timerType ->
val default: TimerInfo = defaultTimerInfo.getValue(timerType)
Button(onClick = { viewModel.onTimerTypeChosen(default, open) }) {
Button(
onClick = { viewModel.onTimerTypeChosen(default, open) },
modifier = Modifier.fillMaxWidth().padding(5.dp)
) {
Text(text = timerType.name)
}
}

View file

@ -70,8 +70,15 @@
<!-- Timers -->
<string name="timers">Timers</string>
<string name="delete_timer">Delete Timer</string>
<string name="edit">Edit</string>
<string name="add_timer">Add timer</string>
<string name="name_error">Name should not be blank</string>
<string name="description_error">Description should not be blank</string>
<string name="fill_out_error">Fill out all the fields correctly!</string>
<string name="pick_time">Select time</string>
<string name="state_focus">Focus!</string>
<plurals name="state_focus_remaining">