#13 and #17 form for creating, editing and deleting tasks

This commit is contained in:
brreynie 2023-05-03 23:18:53 +02:00
parent 6a676c2fad
commit 6765229d37
14 changed files with 249 additions and 30 deletions

View file

@ -44,6 +44,8 @@ import be.ugent.sel.studeez.screens.tasks.SubjectRoute
import be.ugent.sel.studeez.screens.tasks.TaskRoute
import be.ugent.sel.studeez.screens.tasks.forms.SubjectAddRoute
import be.ugent.sel.studeez.screens.tasks.forms.SubjectEditRoute
import be.ugent.sel.studeez.screens.tasks.forms.TaskAddRoute
import be.ugent.sel.studeez.screens.tasks.forms.TaskEditRoute
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewRoute
import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionRoute
import be.ugent.sel.studeez.ui.theme.StudeezTheme
@ -161,7 +163,7 @@ fun StudeezNavGraph(
composable(StudeezDestinations.ADD_SUBJECT_FORM) {
SubjectAddRoute(
goBack = goBack,
open = open,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}
@ -169,7 +171,23 @@ fun StudeezNavGraph(
composable(StudeezDestinations.EDIT_SUBJECT_FORM) {
SubjectEditRoute(
goBack = goBack,
open = open,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}
composable(StudeezDestinations.ADD_TASK_FORM) {
TaskAddRoute(
goBack = goBack,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}
composable(StudeezDestinations.EDIT_TASK_FORM) {
TaskEditRoute(
goBack = goBack,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}

View file

@ -91,10 +91,11 @@ fun StealthButtonCardPreview() {
@Composable
fun DeleteButton(
@StringRes text: Int,
onClick: () -> Unit,
) {
BasicButton(
text = R.string.delete_subject,
text = text,
modifier = Modifier.basicButton(),
onClick = onClick,
colors = ButtonDefaults.buttonColors(
@ -107,7 +108,7 @@ fun DeleteButton(
@Preview
@Composable
fun DeleteButtonPreview() {
DeleteButton {}
DeleteButton(text = R.string.delete_subject) {}
}
@Composable

View file

@ -5,7 +5,7 @@ import javax.inject.Inject
import javax.inject.Singleton
/**
* Used to communicate the selected subject from the subject overview to the task overview of that subject.
* Used to communicate the selected subject from the subject overview other screens.
* Because this is a singleton-class the view-models of both screens observe the same data.
*/
@Singleton
@ -15,4 +15,6 @@ class SelectedSubject @Inject constructor() {
fun set(subject: Subject) {
this.subject = subject
}
fun isSet() = this::subject.isInitialized
}

View file

@ -0,0 +1,21 @@
package be.ugent.sel.studeez.data
import be.ugent.sel.studeez.data.local.models.task.Task
import javax.inject.Inject
import javax.inject.Singleton
/**
* Used to communicate the selected task from the task overview other screens.
* Because this is a singleton-class the view-models of both screens observe the same data.
*/
@Singleton
class SelectedTask @Inject constructor() {
private lateinit var task: Task
operator fun invoke() = task
fun set(task: Task) {
this.task = task
}
fun isSet() = this::task.isInitialized
}

View file

@ -10,6 +10,8 @@ interface TaskDAO {
fun saveTask(newTask: Task)
fun updateTask(newTask: Task)
fun deleteTask(oldTask: Task)
fun toggleTaskCompleted(task: Task, completed: Boolean)

View file

@ -22,7 +22,11 @@ class FireBaseTaskDAO @Inject constructor(
}
override fun saveTask(newTask: Task) {
TODO("Not yet implemented")
selectedSubjectTasksCollection(newTask.subjectId).add(newTask)
}
override fun updateTask(newTask: Task) {
selectedSubjectTasksCollection(newTask.id).document(newTask.id).set(newTask)
}
override fun deleteTask(oldTask: Task) {

View file

@ -15,6 +15,8 @@ object StudeezDestinations {
const val ADD_SUBJECT_FORM = "add_subject"
const val EDIT_SUBJECT_FORM = "edit_subject"
const val TASKS_SCREEN = "tasks"
const val ADD_TASK_FORM = "add_task"
const val EDIT_TASK_FORM = "edit_task"
// const val SESSIONS_SCREEN = "sessions"
const val PROFILE_SCREEN = "profile"

View file

@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
@ -12,15 +11,12 @@ import androidx.compose.material.icons.filled.Edit
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.NewTaskSubjectButton
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.common.composable.tasks.TaskEntry
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.data.local.models.task.Task
import be.ugent.sel.studeez.resources
@ -38,7 +34,7 @@ data class TaskActions(
fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions {
return TaskActions(
addTask = viewModel::addTask,
addTask = { viewModel.addTask(open) },
getTasks = viewModel::getTasks,
getSubject = viewModel::getSelectedSubject,
deleteTask = viewModel::deleteTask,

View file

@ -19,8 +19,8 @@ class TaskViewModel @Inject constructor(
private val selectedSubject: SelectedSubject,
logService: LogService,
) : StudeezViewModel(logService) {
fun addTask() {
fun addTask(open: (String) -> Unit) {
open(StudeezDestinations.ADD_TASK_FORM)
}
fun getTasks(): Flow<List<Task>> {

View file

@ -21,7 +21,7 @@ import be.ugent.sel.studeez.resources
@Composable
fun SubjectAddRoute(
goBack: () -> Unit,
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: SubjectFormViewModel,
) {
val uiState by viewModel.uiState
@ -29,7 +29,7 @@ fun SubjectAddRoute(
title = R.string.new_subject,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onCreate(open) },
onConfirm = { viewModel.onCreate(openAndPopUp) },
onNameChange = viewModel::onNameChange,
onColorChange = {},
)
@ -38,7 +38,7 @@ fun SubjectAddRoute(
@Composable
fun SubjectEditRoute(
goBack: () -> Unit,
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: SubjectFormViewModel,
) {
val uiState by viewModel.uiState
@ -46,11 +46,13 @@ fun SubjectEditRoute(
title = R.string.edit_subject,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onEdit(open) },
onConfirm = { viewModel.onEdit(openAndPopUp) },
onNameChange = viewModel::onNameChange,
onColorChange = {},
) {
DeleteButton(onClick = { viewModel.onDelete(open) })
DeleteButton(text = R.string.delete_subject) {
viewModel.onDelete(openAndPopUp)
}
}
}
@ -112,6 +114,6 @@ fun EditSubjectFormPreview() {
onNameChange = {},
onColorChange = {},
) {
DeleteButton {}
DeleteButton(text = R.string.delete_subject) {}
}
}

View file

@ -16,7 +16,13 @@ class SubjectFormViewModel @Inject constructor(
private val selectedSubject: SelectedSubject,
logService: LogService,
) : StudeezViewModel(logService) {
var uiState = mutableStateOf(SubjectFormUiState())
var uiState = mutableStateOf(
if (selectedSubject.isSet()) SubjectFormUiState(
name = selectedSubject().name,
color = selectedSubject().argb_color
)
else SubjectFormUiState()
)
private set
private val name: String
@ -33,12 +39,12 @@ class SubjectFormViewModel @Inject constructor(
uiState.value = uiState.value.copy(color = newValue)
}
fun onDelete(open: (String) -> Unit) {
fun onDelete(openAndPopUp: (String, String) -> Unit) {
subjectDAO.deleteSubject(selectedSubject())
open(StudeezDestinations.SUBJECT_SCREEN)
openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
}
fun onCreate(open: (String) -> Unit) {
fun onCreate(openAndPopUp: (String, String) -> Unit) {
val newSubject = Subject(
name = name,
argb_color = color,
@ -46,20 +52,18 @@ class SubjectFormViewModel @Inject constructor(
subjectDAO.saveSubject(
newSubject
)
selectedSubject.set(newSubject)
// TODO open newly created subject
// selectedSubject.set(newSubject)
// open(StudeezDestinations.TASKS_SCREEN)
open(StudeezDestinations.SUBJECT_SCREEN)
openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.ADD_SUBJECT_FORM)
}
fun onEdit(open: (String) -> Unit) {
fun onEdit(openAndPopUp: (String, String) -> Unit) {
val newSubject = selectedSubject().copy(
name = name,
argb_color = color,
)
subjectDAO.updateSubject(
newSubject
)
open(StudeezDestinations.TASKS_SCREEN)
subjectDAO.updateSubject(newSubject)
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
}
}

View file

@ -0,0 +1,113 @@
package be.ugent.sel.studeez.screens.tasks.forms
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.DeleteButton
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources
@Composable
fun TaskAddRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TaskFormViewModel,
) {
val uiState by viewModel.uiState
TaskForm(
title = R.string.new_task,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onCreate(openAndPopUp) },
onNameChange = viewModel::onNameChange
)
}
@Composable
fun TaskEditRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TaskFormViewModel,
) {
val uiState by viewModel.uiState
TaskForm(
title = R.string.edit_task,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onEdit(openAndPopUp) },
onNameChange = viewModel::onNameChange
) {
DeleteButton(text = R.string.delete_task) {
viewModel.onDelete(openAndPopUp)
}
}
}
@Composable
fun TaskForm(
@StringRes title: Int,
goBack: () -> Unit,
uiState: TaskFormUiState,
onConfirm: () -> Unit,
onNameChange: (String) -> Unit,
extraButton: @Composable () -> Unit = {}
) {
SecondaryScreenTemplate(
title = resources().getString(title),
popUp = goBack,
) {
Column {
OutlinedTextField(
singleLine = true,
value = uiState.name,
onValueChange = onNameChange,
placeholder = { Text(stringResource(id = R.string.name)) },
modifier = Modifier.fieldModifier(),
)
BasicButton(
text = R.string.confirm,
modifier = Modifier.basicButton(),
onClick = onConfirm,
)
extraButton()
}
}
}
@Preview
@Composable
fun AddTaskFormPreview() {
TaskForm(
title = R.string.new_task,
goBack = {},
uiState = TaskFormUiState(),
onConfirm = {},
onNameChange = {},
)
}
@Preview
@Composable
fun EditTaskFormPreview() {
TaskForm(
title = R.string.edit_task,
goBack = {},
uiState = TaskFormUiState(
name = "Test Task",
),
onConfirm = {},
onNameChange = {},
) {
DeleteButton(text = R.string.delete_task) {}
}
}

View file

@ -0,0 +1,5 @@
package be.ugent.sel.studeez.screens.tasks.forms
data class TaskFormUiState(
val name: String = "",
)

View file

@ -0,0 +1,49 @@
package be.ugent.sel.studeez.screens.tasks.forms
import androidx.compose.runtime.mutableStateOf
import be.ugent.sel.studeez.data.SelectedSubject
import be.ugent.sel.studeez.data.SelectedTask
import be.ugent.sel.studeez.data.local.models.task.Task
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.TaskDAO
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class TaskFormViewModel @Inject constructor(
private val taskDAO: TaskDAO,
private val selectedSubject: SelectedSubject,
private val selectedTask: SelectedTask,
logService: LogService,
) : StudeezViewModel(logService) {
var uiState = mutableStateOf(
if (selectedTask.isSet()) TaskFormUiState(selectedTask().name) else TaskFormUiState()
)
private set
private val name: String
get() = uiState.value.name
fun onNameChange(newValue: String) {
uiState.value = uiState.value.copy(name = newValue)
}
fun onDelete(openAndPopUp: (String, String) -> Unit) {
taskDAO.deleteTask(selectedTask())
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM)
}
fun onCreate(openAndPopUp: (String, String) -> Unit) {
val newTask = Task(name = name, subjectId = selectedSubject().id)
taskDAO.saveTask(newTask)
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.ADD_TASK_FORM)
}
fun onEdit(openAndPopUp: (String, String) -> Unit) {
val newTask = Task(name = name)
taskDAO.updateTask(newTask)
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM)
}
}