diff --git a/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt b/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt index 276e250..0c8ee3c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt +++ b/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt @@ -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(), ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt index 0d6d9e8..a98b24b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt @@ -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 diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt index 6e2da9f..fbc7e48 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt @@ -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 } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt new file mode 100644 index 0000000..9c3f042 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt index 38c639a..0f629ea 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt @@ -10,6 +10,8 @@ interface TaskDAO { fun saveTask(newTask: Task) + fun updateTask(newTask: Task) + fun deleteTask(oldTask: Task) fun toggleTaskCompleted(task: Task, completed: Boolean) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt index 09e8cdf..45d32f8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt @@ -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) { diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index f56b872..5f4053a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -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" diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt index bd41cff..e3ceca7 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt @@ -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, diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt index 26b8c17..138d32c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt @@ -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> { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormScreen.kt index 38ef6f5..f18dc2c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormScreen.kt @@ -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) {} } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormViewModel.kt index ec246f4..68ebd3e 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormViewModel.kt @@ -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) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormScreen.kt new file mode 100644 index 0000000..1d98d3d --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormScreen.kt @@ -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) {} + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormUiState.kt new file mode 100644 index 0000000..d967d59 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormUiState.kt @@ -0,0 +1,5 @@ +package be.ugent.sel.studeez.screens.tasks.forms + +data class TaskFormUiState( + val name: String = "", +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormViewModel.kt new file mode 100644 index 0000000..03ad32b --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormViewModel.kt @@ -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) + } +} \ No newline at end of file