commit
						d22d40439d
					
				
					 24 changed files with 380 additions and 205 deletions
				
			
		|  | @ -2,14 +2,16 @@ package be.ugent.sel.studeez.common.composable.feed | ||||||
| 
 | 
 | ||||||
| import androidx.lifecycle.viewModelScope | import androidx.lifecycle.viewModelScope | ||||||
| import be.ugent.sel.studeez.data.SelectedTask | import be.ugent.sel.studeez.data.SelectedTask | ||||||
| import be.ugent.sel.studeez.data.local.models.FeedEntry |  | ||||||
| import be.ugent.sel.studeez.domain.FeedDAO | import be.ugent.sel.studeez.domain.FeedDAO | ||||||
| import be.ugent.sel.studeez.domain.LogService | import be.ugent.sel.studeez.domain.LogService | ||||||
| import be.ugent.sel.studeez.domain.TaskDAO | import be.ugent.sel.studeez.domain.TaskDAO | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel | import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel | import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
| import kotlinx.coroutines.flow.* | import kotlinx.coroutines.flow.SharingStarted | ||||||
|  | import kotlinx.coroutines.flow.StateFlow | ||||||
|  | import kotlinx.coroutines.flow.map | ||||||
|  | import kotlinx.coroutines.flow.stateIn | ||||||
| import kotlinx.coroutines.launch | import kotlinx.coroutines.launch | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,13 +1,7 @@ | ||||||
| package be.ugent.sel.studeez.common.composable.tasks | package be.ugent.sel.studeez.common.composable.tasks | ||||||
| 
 | 
 | ||||||
| import androidx.compose.foundation.background | import androidx.compose.foundation.background | ||||||
| import androidx.compose.foundation.layout.Arrangement | import androidx.compose.foundation.layout.* | ||||||
| import androidx.compose.foundation.layout.Box |  | ||||||
| import androidx.compose.foundation.layout.Column |  | ||||||
| import androidx.compose.foundation.layout.Row |  | ||||||
| import androidx.compose.foundation.layout.fillMaxWidth |  | ||||||
| import androidx.compose.foundation.layout.padding |  | ||||||
| import androidx.compose.foundation.layout.size |  | ||||||
| import androidx.compose.foundation.shape.CircleShape | import androidx.compose.foundation.shape.CircleShape | ||||||
| import androidx.compose.material.Card | import androidx.compose.material.Card | ||||||
| import androidx.compose.material.Icon | import androidx.compose.material.Icon | ||||||
|  | @ -15,6 +9,8 @@ import androidx.compose.material.Text | ||||||
| import androidx.compose.material.icons.Icons | import androidx.compose.material.icons.Icons | ||||||
| import androidx.compose.material.icons.filled.List | import androidx.compose.material.icons.filled.List | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.collectAsState | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
| import androidx.compose.ui.Alignment | import androidx.compose.ui.Alignment | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
| import androidx.compose.ui.draw.clip | import androidx.compose.ui.draw.clip | ||||||
|  | @ -24,16 +20,20 @@ import androidx.compose.ui.text.font.FontWeight | ||||||
| import androidx.compose.ui.text.style.TextOverflow | import androidx.compose.ui.text.style.TextOverflow | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
| import androidx.compose.ui.unit.dp | import androidx.compose.ui.unit.dp | ||||||
| import be.ugent.sel.studeez.R.string as AppText |  | ||||||
| import be.ugent.sel.studeez.common.composable.StealthButton | import be.ugent.sel.studeez.common.composable.StealthButton | ||||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | import be.ugent.sel.studeez.data.local.models.task.Subject | ||||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds | import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds | ||||||
|  | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.flowOf | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
| @Composable | @Composable | ||||||
| fun SubjectEntry( | fun SubjectEntry( | ||||||
|     subject: Subject, |     subject: Subject, | ||||||
|     onViewSubject: () -> Unit, |     onViewSubject: () -> Unit, | ||||||
|  |     getStudyTime: () -> Flow<Int>, | ||||||
| ) { | ) { | ||||||
|  |     val studytime by getStudyTime().collectAsState(initial = 0) | ||||||
|     Card( |     Card( | ||||||
|         modifier = Modifier |         modifier = Modifier | ||||||
|             .fillMaxWidth() |             .fillMaxWidth() | ||||||
|  | @ -70,7 +70,7 @@ fun SubjectEntry( | ||||||
|                         verticalAlignment = Alignment.CenterVertically, |                         verticalAlignment = Alignment.CenterVertically, | ||||||
|                     ) { |                     ) { | ||||||
|                         Text( |                         Text( | ||||||
|                             text = HoursMinutesSeconds(subject.time).toString(), |                             text = HoursMinutesSeconds(studytime).toString(), | ||||||
|                         ) |                         ) | ||||||
|                         Row( |                         Row( | ||||||
|                             verticalAlignment = Alignment.CenterVertically, |                             verticalAlignment = Alignment.CenterVertically, | ||||||
|  | @ -80,7 +80,7 @@ fun SubjectEntry( | ||||||
|                                 imageVector = Icons.Default.List, |                                 imageVector = Icons.Default.List, | ||||||
|                                 contentDescription = stringResource(id = AppText.tasks) |                                 contentDescription = stringResource(id = AppText.tasks) | ||||||
|                             ) |                             ) | ||||||
|                             Text(text = "0/0") // TODO |                             Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | @ -104,9 +104,12 @@ fun SubjectEntryPreview() { | ||||||
|         subject = Subject( |         subject = Subject( | ||||||
|             name = "Test Subject", |             name = "Test Subject", | ||||||
|             argb_color = 0xFFFFD200, |             argb_color = 0xFFFFD200, | ||||||
|             time = 60 |             taskCount = 5, | ||||||
|  |             taskCompletedCount = 2, | ||||||
|         ), |         ), | ||||||
|     ) {} |         onViewSubject = {}, | ||||||
|  |         getStudyTime = { flowOf() } | ||||||
|  |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Preview | @Preview | ||||||
|  | @ -116,7 +119,8 @@ fun OverflowSubjectEntryPreview() { | ||||||
|         subject = Subject( |         subject = Subject( | ||||||
|             name = "Testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", |             name = "Testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", | ||||||
|             argb_color = 0xFFFFD200, |             argb_color = 0xFFFFD200, | ||||||
|             time = 60 |  | ||||||
|         ), |         ), | ||||||
|     ) {} |         onViewSubject = {}, | ||||||
|  |         getStudyTime = { flowOf() } | ||||||
|  |     ) | ||||||
| } | } | ||||||
|  | @ -1,17 +1,7 @@ | ||||||
| package be.ugent.sel.studeez.common.composable.tasks | package be.ugent.sel.studeez.common.composable.tasks | ||||||
| 
 | 
 | ||||||
| import androidx.compose.foundation.layout.Arrangement | import androidx.compose.foundation.layout.* | ||||||
| import androidx.compose.foundation.layout.Box | import androidx.compose.material.* | ||||||
| import androidx.compose.foundation.layout.Row |  | ||||||
| import androidx.compose.foundation.layout.fillMaxWidth |  | ||||||
| import androidx.compose.foundation.layout.padding |  | ||||||
| import androidx.compose.material.Card |  | ||||||
| import androidx.compose.material.Checkbox |  | ||||||
| import androidx.compose.material.CheckboxDefaults |  | ||||||
| import androidx.compose.material.Icon |  | ||||||
| import androidx.compose.material.IconButton |  | ||||||
| import androidx.compose.material.MaterialTheme |  | ||||||
| import androidx.compose.material.Text |  | ||||||
| import androidx.compose.material.icons.Icons | import androidx.compose.material.icons.Icons | ||||||
| import androidx.compose.material.icons.filled.Delete | import androidx.compose.material.icons.filled.Delete | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
|  | @ -31,7 +21,7 @@ import be.ugent.sel.studeez.resources | ||||||
| fun TaskEntry( | fun TaskEntry( | ||||||
|     task: Task, |     task: Task, | ||||||
|     onCheckTask: (Boolean) -> Unit, |     onCheckTask: (Boolean) -> Unit, | ||||||
|     onDeleteTask: () -> Unit, |     onArchiveTask: () -> Unit, | ||||||
|     onStartTask: () -> Unit |     onStartTask: () -> Unit | ||||||
| ) { | ) { | ||||||
|     Card( |     Card( | ||||||
|  | @ -81,7 +71,7 @@ fun TaskEntry( | ||||||
|             Box(modifier = Modifier.weight(7f)) { |             Box(modifier = Modifier.weight(7f)) { | ||||||
|                 if (task.completed) { |                 if (task.completed) { | ||||||
|                     IconButton( |                     IconButton( | ||||||
|                         onClick = onDeleteTask, |                         onClick = onArchiveTask, | ||||||
|                         modifier = Modifier |                         modifier = Modifier | ||||||
|                             .padding(start = 20.dp) |                             .padding(start = 20.dp) | ||||||
|                     ) { |                     ) { | ||||||
|  |  | ||||||
|  | @ -1,10 +1,22 @@ | ||||||
| package be.ugent.sel.studeez.data.local.models.task | package be.ugent.sel.studeez.data.local.models.task | ||||||
| 
 | 
 | ||||||
| import com.google.firebase.firestore.DocumentId | import com.google.firebase.firestore.DocumentId | ||||||
|  | import com.google.firebase.firestore.Exclude | ||||||
| 
 | 
 | ||||||
| data class Subject( | data class Subject( | ||||||
|     @DocumentId val id: String = "", |     @DocumentId val id: String = "", | ||||||
|     val name: String = "", |     val name: String = "", | ||||||
|     val time: Int = 0, |  | ||||||
|     val argb_color: Long = 0, |     val argb_color: Long = 0, | ||||||
|  |     var archived: Boolean = false, | ||||||
|  |     @get:Exclude @set:Exclude | ||||||
|  |     var taskCount: Int = 0, | ||||||
|  |     @get:Exclude @set:Exclude | ||||||
|  |     var taskCompletedCount: Int = 0, | ||||||
| ) | ) | ||||||
|  | 
 | ||||||
|  | object SubjectDocument { | ||||||
|  |     const val id = "id" | ||||||
|  |     const val name = "name" | ||||||
|  |     const val archived = "archived" | ||||||
|  |     const val argb_color = "argb_color" | ||||||
|  | } | ||||||
|  | @ -5,10 +5,15 @@ import com.google.firebase.firestore.DocumentId | ||||||
| data class Task( | data class Task( | ||||||
|     @DocumentId val id: String = "", |     @DocumentId val id: String = "", | ||||||
|     val name: String = "", |     val name: String = "", | ||||||
|     val completed: Boolean = false, |     var completed: Boolean = false, | ||||||
|     val time: Int = 0, |     val time: Int = 0, | ||||||
|     val subjectId: String = "", |     val subjectId: String = "", | ||||||
| ) |     var archived: Boolean = false, | ||||||
|  | ) { | ||||||
|  |     fun archive() { | ||||||
|  |         this.archived = true | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| object TaskDocument { | object TaskDocument { | ||||||
|     const val id = "id" |     const val id = "id" | ||||||
|  | @ -16,4 +21,5 @@ object TaskDocument { | ||||||
|     const val completed = "completed" |     const val completed = "completed" | ||||||
|     const val time = "time" |     const val time = "time" | ||||||
|     const val subjectId = "subjectId" |     const val subjectId = "subjectId" | ||||||
|  |     const val archived = "archived" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,5 +12,10 @@ interface SubjectDAO { | ||||||
|     fun deleteSubject(oldSubject: Subject) |     fun deleteSubject(oldSubject: Subject) | ||||||
| 
 | 
 | ||||||
|     fun updateSubject(newSubject: Subject) |     fun updateSubject(newSubject: Subject) | ||||||
|  | 
 | ||||||
|  |     suspend fun getTaskCount(subject: Subject): Int | ||||||
|  |     suspend fun getCompletedTaskCount(subject: Subject): Int | ||||||
|  |     fun getStudyTime(subject: Subject): Flow<Int> | ||||||
|  | 
 | ||||||
|     suspend fun getSubject(subjectId: String): Subject? |     suspend fun getSubject(subjectId: String): Subject? | ||||||
| } | } | ||||||
|  | @ -14,7 +14,5 @@ interface TaskDAO { | ||||||
| 
 | 
 | ||||||
|     fun deleteTask(oldTask: Task) |     fun deleteTask(oldTask: Task) | ||||||
| 
 | 
 | ||||||
|     fun toggleTaskCompleted(task: Task, completed: Boolean) |  | ||||||
| 
 |  | ||||||
|     suspend fun getTask(subjectId: String, taskId: String): Task |     suspend fun getTask(subjectId: String, taskId: String): Task | ||||||
| } | } | ||||||
|  | @ -1,11 +1,14 @@ | ||||||
| package be.ugent.sel.studeez.domain.implementation | package be.ugent.sel.studeez.domain.implementation | ||||||
| 
 | 
 | ||||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | 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.data.local.models.task.SubjectDocument | ||||||
| import be.ugent.sel.studeez.domain.AccountDAO | import be.ugent.sel.studeez.domain.AccountDAO | ||||||
| import be.ugent.sel.studeez.domain.SubjectDAO | import be.ugent.sel.studeez.domain.SubjectDAO | ||||||
|  | import be.ugent.sel.studeez.domain.TaskDAO | ||||||
|  | import com.google.firebase.firestore.AggregateSource | ||||||
| import com.google.firebase.firestore.CollectionReference | import com.google.firebase.firestore.CollectionReference | ||||||
| import com.google.firebase.firestore.FirebaseFirestore | import com.google.firebase.firestore.FirebaseFirestore | ||||||
|  | import com.google.firebase.firestore.Query | ||||||
| import com.google.firebase.firestore.ktx.snapshots | import com.google.firebase.firestore.ktx.snapshots | ||||||
| import com.google.firebase.firestore.ktx.toObject | import com.google.firebase.firestore.ktx.toObject | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
|  | @ -16,11 +19,20 @@ import javax.inject.Inject | ||||||
| class FireBaseSubjectDAO @Inject constructor( | class FireBaseSubjectDAO @Inject constructor( | ||||||
|     private val firestore: FirebaseFirestore, |     private val firestore: FirebaseFirestore, | ||||||
|     private val auth: AccountDAO, |     private val auth: AccountDAO, | ||||||
|  |     private val taskDAO: TaskDAO, | ||||||
| ) : SubjectDAO { | ) : SubjectDAO { | ||||||
|     override fun getSubjects(): Flow<List<Subject>> { |     override fun getSubjects(): Flow<List<Subject>> { | ||||||
|         return currentUserSubjectsCollection() |         return currentUserSubjectsCollection() | ||||||
|  |             .subjectNotArchived() | ||||||
|             .snapshots() |             .snapshots() | ||||||
|             .map { it.toObjects(Subject::class.java) } |             .map { it.toObjects(Subject::class.java) } | ||||||
|  |             .map { subjects -> | ||||||
|  |                 subjects.map { subject -> | ||||||
|  |                     subject.taskCount = getTaskCount(subject) | ||||||
|  |                     subject.taskCompletedCount = getCompletedTaskCount(subject) | ||||||
|  |                     subject | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun getSubject(subjectId: String): Subject? { |     override suspend fun getSubject(subjectId: String): Subject? { | ||||||
|  | @ -39,8 +51,45 @@ class FireBaseSubjectDAO @Inject constructor( | ||||||
|         currentUserSubjectsCollection().document(newSubject.id).set(newSubject) |         currentUserSubjectsCollection().document(newSubject.id).set(newSubject) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     override suspend fun getTaskCount(subject: Subject): Int { | ||||||
|  |         return subjectTasksCollection(subject) | ||||||
|  |             .taskNotArchived() | ||||||
|  |             .count() | ||||||
|  |             .get(AggregateSource.SERVER) | ||||||
|  |             .await() | ||||||
|  |             .count.toInt() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun getCompletedTaskCount(subject: Subject): Int { | ||||||
|  |         return subjectTasksCollection(subject) | ||||||
|  |             .taskNotArchived() | ||||||
|  |             .taskNotCompleted() | ||||||
|  |             .count() | ||||||
|  |             .get(AggregateSource.SERVER) | ||||||
|  |             .await() | ||||||
|  |             .count.toInt() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun getStudyTime(subject: Subject): Flow<Int> { | ||||||
|  |         return taskDAO.getTasks(subject) | ||||||
|  |             .map { tasks -> tasks.sumOf { it.time } } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun currentUserSubjectsCollection(): CollectionReference = |     private fun currentUserSubjectsCollection(): CollectionReference = | ||||||
|         firestore.collection(FireBaseCollections.USER_COLLECTION) |         firestore.collection(FireBaseCollections.USER_COLLECTION) | ||||||
|             .document(auth.currentUserId) |             .document(auth.currentUserId) | ||||||
|             .collection(FireBaseCollections.SUBJECT_COLLECTION) |             .collection(FireBaseCollections.SUBJECT_COLLECTION) | ||||||
|  | 
 | ||||||
|  |     private fun subjectTasksCollection(subject: Subject): CollectionReference = | ||||||
|  |         firestore.collection(FireBaseCollections.USER_COLLECTION) | ||||||
|  |             .document(auth.currentUserId) | ||||||
|  |             .collection(FireBaseCollections.SUBJECT_COLLECTION) | ||||||
|  |             .document(subject.id) | ||||||
|  |             .collection(FireBaseCollections.TASK_COLLECTION) | ||||||
|  | 
 | ||||||
|  |     fun CollectionReference.subjectNotArchived(): Query = | ||||||
|  |         this.whereEqualTo(SubjectDocument.archived, false) | ||||||
|  | 
 | ||||||
|  |     fun Query.subjectNotArchived(): Query = | ||||||
|  |         this.whereEqualTo(SubjectDocument.archived, false) | ||||||
| } | } | ||||||
|  | @ -7,10 +7,10 @@ import be.ugent.sel.studeez.domain.AccountDAO | ||||||
| import be.ugent.sel.studeez.domain.TaskDAO | import be.ugent.sel.studeez.domain.TaskDAO | ||||||
| import com.google.firebase.firestore.CollectionReference | import com.google.firebase.firestore.CollectionReference | ||||||
| import com.google.firebase.firestore.FirebaseFirestore | import com.google.firebase.firestore.FirebaseFirestore | ||||||
|  | import com.google.firebase.firestore.Query | ||||||
| import com.google.firebase.firestore.ktx.snapshots | import com.google.firebase.firestore.ktx.snapshots | ||||||
| import com.google.firebase.firestore.ktx.toObject | import com.google.firebase.firestore.ktx.toObject | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
| import kotlinx.coroutines.flow.flow |  | ||||||
| import kotlinx.coroutines.flow.map | import kotlinx.coroutines.flow.map | ||||||
| import kotlinx.coroutines.tasks.await | import kotlinx.coroutines.tasks.await | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
|  | @ -21,6 +21,7 @@ class FireBaseTaskDAO @Inject constructor( | ||||||
| ) : TaskDAO { | ) : TaskDAO { | ||||||
|     override fun getTasks(subject: Subject): Flow<List<Task>> { |     override fun getTasks(subject: Subject): Flow<List<Task>> { | ||||||
|         return selectedSubjectTasksCollection(subject.id) |         return selectedSubjectTasksCollection(subject.id) | ||||||
|  |             .taskNotArchived() | ||||||
|             .snapshots() |             .snapshots() | ||||||
|             .map { it.toObjects(Task::class.java) } |             .map { it.toObjects(Task::class.java) } | ||||||
|     } |     } | ||||||
|  | @ -34,23 +35,34 @@ class FireBaseTaskDAO @Inject constructor( | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun updateTask(newTask: Task) { |     override fun updateTask(newTask: Task) { | ||||||
|         selectedSubjectTasksCollection(newTask.id).document(newTask.id).set(newTask) |         selectedSubjectTasksCollection(newTask.subjectId) | ||||||
|  |             .document(newTask.id) | ||||||
|  |             .set(newTask) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun deleteTask(oldTask: Task) { |     override fun deleteTask(oldTask: Task) { | ||||||
|         selectedSubjectTasksCollection(oldTask.subjectId).document(oldTask.id).delete() |         selectedSubjectTasksCollection(oldTask.subjectId).document(oldTask.id).delete() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun toggleTaskCompleted(task: Task, completed: Boolean) { |  | ||||||
|         selectedSubjectTasksCollection(task.subjectId) |  | ||||||
|             .document(task.id) |  | ||||||
|             .update(TaskDocument.completed, completed) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference = |     private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference = | ||||||
|         firestore.collection(FireBaseCollections.USER_COLLECTION) |         firestore.collection(FireBaseCollections.USER_COLLECTION) | ||||||
|             .document(auth.currentUserId) |             .document(auth.currentUserId) | ||||||
|             .collection(FireBaseCollections.SUBJECT_COLLECTION) |             .collection(FireBaseCollections.SUBJECT_COLLECTION) | ||||||
|             .document(subjectId) |             .document(subjectId) | ||||||
|             .collection(FireBaseCollections.TASK_COLLECTION) |             .collection(FireBaseCollections.TASK_COLLECTION) | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // Extend CollectionReference and Query with some filters | ||||||
|  | 
 | ||||||
|  | fun CollectionReference.taskNotArchived(): Query = | ||||||
|  |     this.whereEqualTo(TaskDocument.archived, false) | ||||||
|  | 
 | ||||||
|  | fun Query.taskNotArchived(): Query = | ||||||
|  |     this.whereEqualTo(TaskDocument.archived, false) | ||||||
|  | 
 | ||||||
|  | fun CollectionReference.taskNotCompleted(): Query = | ||||||
|  |     this.whereEqualTo(TaskDocument.completed, true) | ||||||
|  | 
 | ||||||
|  | fun Query.taskNotCompleted(): Query = | ||||||
|  |     this.whereEqualTo(TaskDocument.completed, true) | ||||||
|  |  | ||||||
|  | @ -7,15 +7,17 @@ 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.data.local.models.task.Task | ||||||
| import be.ugent.sel.studeez.domain.FeedDAO | import be.ugent.sel.studeez.domain.FeedDAO | ||||||
| import be.ugent.sel.studeez.domain.SessionDAO | import be.ugent.sel.studeez.domain.SessionDAO | ||||||
|  | import be.ugent.sel.studeez.domain.SubjectDAO | ||||||
| import be.ugent.sel.studeez.domain.TaskDAO | import be.ugent.sel.studeez.domain.TaskDAO | ||||||
| import com.google.firebase.Timestamp | import com.google.firebase.Timestamp | ||||||
| import kotlinx.coroutines.flow.* | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.map | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| class FirebaseFeedDAO @Inject constructor( | class FirebaseFeedDAO @Inject constructor( | ||||||
|     private val sessionDAO: SessionDAO, |     private val sessionDAO: SessionDAO, | ||||||
|     private val taskDAO: TaskDAO, |     private val taskDAO: TaskDAO, | ||||||
|     private val subjectDAO: FireBaseSubjectDAO |     private val subjectDAO: SubjectDAO | ||||||
| ) : FeedDAO { | ) : FeedDAO { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -24,12 +24,12 @@ import be.ugent.sel.studeez.screens.sessions.SessionsRoute | ||||||
| import be.ugent.sel.studeez.screens.settings.SettingsRoute | import be.ugent.sel.studeez.screens.settings.SettingsRoute | ||||||
| import be.ugent.sel.studeez.screens.sign_up.SignUpRoute | import be.ugent.sel.studeez.screens.sign_up.SignUpRoute | ||||||
| import be.ugent.sel.studeez.screens.splash.SplashRoute | import be.ugent.sel.studeez.screens.splash.SplashRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.SubjectRoute | import be.ugent.sel.studeez.screens.subjects.SubjectRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.TaskRoute | import be.ugent.sel.studeez.screens.tasks.TaskRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.forms.SubjectAddRoute | import be.ugent.sel.studeez.screens.subjects.form.SubjectCreateRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.forms.SubjectEditRoute | import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.forms.TaskAddRoute | import be.ugent.sel.studeez.screens.tasks.form.TaskCreateRoute | ||||||
| import be.ugent.sel.studeez.screens.tasks.forms.TaskEditRoute | import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute | ||||||
| import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute | import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute | ||||||
| import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute | import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute | ||||||
| import be.ugent.sel.studeez.screens.timer_form.timer_type_select.TimerTypeSelectScreen | import be.ugent.sel.studeez.screens.timer_form.timer_type_select.TimerTypeSelectScreen | ||||||
|  | @ -81,7 +81,7 @@ fun StudeezNavGraph( | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         composable(StudeezDestinations.ADD_SUBJECT_FORM) { |         composable(StudeezDestinations.ADD_SUBJECT_FORM) { | ||||||
|             SubjectAddRoute( |             SubjectCreateRoute( | ||||||
|                 goBack = goBack, |                 goBack = goBack, | ||||||
|                 openAndPopUp = openAndPopUp, |                 openAndPopUp = openAndPopUp, | ||||||
|                 viewModel = hiltViewModel(), |                 viewModel = hiltViewModel(), | ||||||
|  | @ -105,7 +105,7 @@ fun StudeezNavGraph( | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         composable(StudeezDestinations.ADD_TASK_FORM) { |         composable(StudeezDestinations.ADD_TASK_FORM) { | ||||||
|             TaskAddRoute( |             TaskCreateRoute( | ||||||
|                 goBack = goBack, |                 goBack = goBack, | ||||||
|                 openAndPopUp = openAndPopUp, |                 openAndPopUp = openAndPopUp, | ||||||
|                 viewModel = hiltViewModel(), |                 viewModel = hiltViewModel(), | ||||||
|  |  | ||||||
|  | @ -1,9 +1,11 @@ | ||||||
| package be.ugent.sel.studeez.screens.session_recap | package be.ugent.sel.studeez.screens.session_recap | ||||||
| 
 | 
 | ||||||
|  | import be.ugent.sel.studeez.data.SelectedTask | ||||||
| import be.ugent.sel.studeez.data.SessionReportState | import be.ugent.sel.studeez.data.SessionReportState | ||||||
| import be.ugent.sel.studeez.data.local.models.SessionReport | import be.ugent.sel.studeez.data.local.models.SessionReport | ||||||
| import be.ugent.sel.studeez.domain.LogService | import be.ugent.sel.studeez.domain.LogService | ||||||
| import be.ugent.sel.studeez.domain.SessionDAO | import be.ugent.sel.studeez.domain.SessionDAO | ||||||
|  | import be.ugent.sel.studeez.domain.TaskDAO | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel | import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel | import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
|  | @ -13,6 +15,8 @@ import javax.inject.Inject | ||||||
| class SessionRecapViewModel @Inject constructor( | class SessionRecapViewModel @Inject constructor( | ||||||
|     sessionReportState: SessionReportState, |     sessionReportState: SessionReportState, | ||||||
|     private val sessionDAO: SessionDAO, |     private val sessionDAO: SessionDAO, | ||||||
|  |     private val taskDAO: TaskDAO, | ||||||
|  |     private val selectedTask: SelectedTask, | ||||||
|     logService: LogService |     logService: LogService | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
| 
 | 
 | ||||||
|  | @ -24,6 +28,8 @@ class SessionRecapViewModel @Inject constructor( | ||||||
| 
 | 
 | ||||||
|     fun saveSession(open: (String, String) -> Unit) { |     fun saveSession(open: (String, String) -> Unit) { | ||||||
|         sessionDAO.saveSession(getSessionReport()) |         sessionDAO.saveSession(getSessionReport()) | ||||||
|  |         val newTask = selectedTask().copy(time = selectedTask().time + report.studyTime) | ||||||
|  |         taskDAO.updateTask(newTask) | ||||||
|         open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) |         open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,121 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.subjects | ||||||
|  | 
 | ||||||
|  | import androidx.compose.foundation.layout.* | ||||||
|  | import androidx.compose.foundation.lazy.LazyColumn | ||||||
|  | import androidx.compose.foundation.lazy.items | ||||||
|  | import androidx.compose.material.CircularProgressIndicator | ||||||
|  | import androidx.compose.material.MaterialTheme | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.collectAsState | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import be.ugent.sel.studeez.common.composable.NewTaskSubjectButton | ||||||
|  | import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate | ||||||
|  | import be.ugent.sel.studeez.common.composable.drawer.DrawerActions | ||||||
|  | import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions | ||||||
|  | import be.ugent.sel.studeez.common.composable.tasks.SubjectEntry | ||||||
|  | import be.ugent.sel.studeez.data.local.models.task.Subject | ||||||
|  | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.flowOf | ||||||
|  | import be.ugent.sel.studeez.R.string as AppText | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun SubjectRoute( | ||||||
|  |     open: (String) -> Unit, | ||||||
|  |     viewModel: SubjectViewModel, | ||||||
|  |     drawerActions: DrawerActions, | ||||||
|  |     navigationBarActions: NavigationBarActions, | ||||||
|  | ) { | ||||||
|  |     val uiState by viewModel.uiState.collectAsState() | ||||||
|  |     SubjectScreen( | ||||||
|  |         drawerActions = drawerActions, | ||||||
|  |         navigationBarActions = navigationBarActions, | ||||||
|  |         onAddSubject = { viewModel.onAddSubject(open) }, | ||||||
|  |         onViewSubject = { viewModel.onViewSubject(it, open) }, | ||||||
|  |         getStudyTime = viewModel::getStudyTime, | ||||||
|  |         uiState, | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun SubjectScreen( | ||||||
|  |     drawerActions: DrawerActions, | ||||||
|  |     navigationBarActions: NavigationBarActions, | ||||||
|  |     onAddSubject: () -> Unit, | ||||||
|  |     onViewSubject: (Subject) -> Unit, | ||||||
|  |     getStudyTime: (Subject) -> Flow<Int>, | ||||||
|  |     uiState: SubjectUiState, | ||||||
|  | ) { | ||||||
|  |     PrimaryScreenTemplate( | ||||||
|  |         title = stringResource(AppText.my_subjects), | ||||||
|  |         drawerActions = drawerActions, | ||||||
|  |         navigationBarActions = navigationBarActions, | ||||||
|  |         barAction = {}, | ||||||
|  |     ) { | ||||||
|  |         when (uiState) { | ||||||
|  |             SubjectUiState.Loading -> Column( | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .fillMaxWidth() | ||||||
|  |                     .fillMaxHeight(), | ||||||
|  |                 verticalArrangement = Arrangement.Center, | ||||||
|  |                 horizontalAlignment = Alignment.CenterHorizontally | ||||||
|  |             ) { | ||||||
|  |                 CircularProgressIndicator(color = MaterialTheme.colors.onBackground) | ||||||
|  |             } | ||||||
|  |             is SubjectUiState.Succes -> { | ||||||
|  |                 Column( | ||||||
|  |                     modifier = Modifier.padding(top = 5.dp) | ||||||
|  |                 ) { | ||||||
|  |                     NewTaskSubjectButton(onClick = onAddSubject, AppText.new_subject) | ||||||
|  |                     LazyColumn { | ||||||
|  |                         items(uiState.subjects) { | ||||||
|  |                             SubjectEntry( | ||||||
|  |                                 subject = it, | ||||||
|  |                                 onViewSubject = { onViewSubject(it) }, | ||||||
|  |                                 getStudyTime = { getStudyTime(it) }, | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun SubjectScreenPreview() { | ||||||
|  |     SubjectScreen( | ||||||
|  |         drawerActions = DrawerActions({}, {}, {}, {}, {}), | ||||||
|  |         navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), | ||||||
|  |         onAddSubject = {}, | ||||||
|  |         onViewSubject = {}, | ||||||
|  |         getStudyTime = { flowOf() }, | ||||||
|  |         uiState = SubjectUiState.Succes( | ||||||
|  |             listOf( | ||||||
|  |                 Subject( | ||||||
|  |                     name = "Test Subject", | ||||||
|  |                     argb_color = 0xFFFFD200, | ||||||
|  |                     taskCount = 5, taskCompletedCount = 2, | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun SubjectScreenLoadingPreview() { | ||||||
|  |     SubjectScreen( | ||||||
|  |         drawerActions = DrawerActions({}, {}, {}, {}, {}), | ||||||
|  |         navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), | ||||||
|  |         onAddSubject = {}, | ||||||
|  |         onViewSubject = {}, | ||||||
|  |         getStudyTime = { flowOf() }, | ||||||
|  |         uiState = SubjectUiState.Loading | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | package be.ugent.sel.studeez.screens.subjects | ||||||
|  | 
 | ||||||
|  | import be.ugent.sel.studeez.data.local.models.task.Subject | ||||||
|  | 
 | ||||||
|  | sealed interface SubjectUiState { | ||||||
|  |     object Loading : SubjectUiState | ||||||
|  |     data class Succes(val subjects: List<Subject>) : SubjectUiState | ||||||
|  | } | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks | package be.ugent.sel.studeez.screens.subjects | ||||||
| 
 | 
 | ||||||
|  | import androidx.lifecycle.viewModelScope | ||||||
| import be.ugent.sel.studeez.data.SelectedSubject | import be.ugent.sel.studeez.data.SelectedSubject | ||||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | import be.ugent.sel.studeez.data.local.models.task.Subject | ||||||
| import be.ugent.sel.studeez.domain.LogService | import be.ugent.sel.studeez.domain.LogService | ||||||
|  | @ -7,7 +8,7 @@ import be.ugent.sel.studeez.domain.SubjectDAO | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel | import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel | import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.* | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| @HiltViewModel | @HiltViewModel | ||||||
|  | @ -16,12 +17,21 @@ class SubjectViewModel @Inject constructor( | ||||||
|     private val selectedSubject: SelectedSubject, |     private val selectedSubject: SelectedSubject, | ||||||
|     logService: LogService, |     logService: LogService, | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
|     fun addSubject(open: (String) -> Unit) { | 
 | ||||||
|  |     val uiState: StateFlow<SubjectUiState> = subjectDAO.getSubjects() | ||||||
|  |         .map { SubjectUiState.Succes(it) } | ||||||
|  |         .stateIn( | ||||||
|  |             scope = viewModelScope, | ||||||
|  |             initialValue = SubjectUiState.Loading, | ||||||
|  |             started = SharingStarted.Eagerly, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     fun onAddSubject(open: (String) -> Unit) { | ||||||
|         open(StudeezDestinations.ADD_SUBJECT_FORM) |         open(StudeezDestinations.ADD_SUBJECT_FORM) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun getSubjects(): Flow<List<Subject>> { |     fun getStudyTime(subject: Subject): Flow<Int> { | ||||||
|         return subjectDAO.getSubjects() |         return subjectDAO.getStudyTime(subject) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun onViewSubject(subject: Subject, open: (String) -> Unit) { |     fun onViewSubject(subject: Subject, open: (String) -> Unit) { | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks.forms | package be.ugent.sel.studeez.screens.subjects.form | ||||||
| 
 | 
 | ||||||
| import androidx.annotation.StringRes | import androidx.annotation.StringRes | ||||||
| import androidx.compose.foundation.layout.Column | import androidx.compose.foundation.layout.Column | ||||||
|  | @ -19,10 +19,10 @@ import be.ugent.sel.studeez.resources | ||||||
| import be.ugent.sel.studeez.R.string as AppText | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
| @Composable | @Composable | ||||||
| fun SubjectAddRoute( | fun SubjectCreateRoute( | ||||||
|     goBack: () -> Unit, |     goBack: () -> Unit, | ||||||
|     openAndPopUp: (String, String) -> Unit, |     openAndPopUp: (String, String) -> Unit, | ||||||
|     viewModel: SubjectFormViewModel, |     viewModel: SubjectCreateFormViewModel, | ||||||
| ) { | ) { | ||||||
|     val uiState by viewModel.uiState |     val uiState by viewModel.uiState | ||||||
|     SubjectForm( |     SubjectForm( | ||||||
|  | @ -39,7 +39,7 @@ fun SubjectAddRoute( | ||||||
| fun SubjectEditRoute( | fun SubjectEditRoute( | ||||||
|     goBack: () -> Unit, |     goBack: () -> Unit, | ||||||
|     openAndPopUp: (String, String) -> Unit, |     openAndPopUp: (String, String) -> Unit, | ||||||
|     viewModel: SubjectFormViewModel, |     viewModel: SubjectEditFormViewModel, | ||||||
| ) { | ) { | ||||||
|     val uiState by viewModel.uiState |     val uiState by viewModel.uiState | ||||||
|     SubjectForm( |     SubjectForm( | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks.forms | package be.ugent.sel.studeez.screens.subjects.form | ||||||
| 
 | 
 | ||||||
| data class SubjectFormUiState( | data class SubjectFormUiState( | ||||||
|     val name: String = "", |     val name: String = "", | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks.forms | package be.ugent.sel.studeez.screens.subjects.form | ||||||
| 
 | 
 | ||||||
|  | import androidx.compose.runtime.MutableState | ||||||
| import androidx.compose.runtime.mutableStateOf | import androidx.compose.runtime.mutableStateOf | ||||||
| import be.ugent.sel.studeez.data.SelectedSubject | import be.ugent.sel.studeez.data.SelectedSubject | ||||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | import be.ugent.sel.studeez.data.local.models.task.Subject | ||||||
|  | @ -10,25 +11,17 @@ import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel | import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| @HiltViewModel | abstract class SubjectFormViewModel( | ||||||
| class SubjectFormViewModel @Inject constructor( |     protected val subjectDAO: SubjectDAO, | ||||||
|     private val subjectDAO: SubjectDAO, |     protected val selectedSubject: SelectedSubject, | ||||||
|     private val selectedSubject: SelectedSubject, |  | ||||||
|     logService: LogService, |     logService: LogService, | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
|     var uiState = mutableStateOf( |     abstract val uiState: MutableState<SubjectFormUiState> | ||||||
|         if (selectedSubject.isSet()) SubjectFormUiState( |  | ||||||
|             name = selectedSubject().name, |  | ||||||
|             color = selectedSubject().argb_color |  | ||||||
|         ) |  | ||||||
|         else SubjectFormUiState() |  | ||||||
|     ) |  | ||||||
|         private set |  | ||||||
| 
 | 
 | ||||||
|     private val name: String |     protected val name: String | ||||||
|         get() = uiState.value.name |         get() = uiState.value.name | ||||||
| 
 | 
 | ||||||
|     private val color: Long |     protected val color: Long | ||||||
|         get() = uiState.value.color |         get() = uiState.value.color | ||||||
| 
 | 
 | ||||||
|     fun onNameChange(newValue: String) { |     fun onNameChange(newValue: String) { | ||||||
|  | @ -38,12 +31,16 @@ class SubjectFormViewModel @Inject constructor( | ||||||
|     fun onColorChange(newValue: Long) { |     fun onColorChange(newValue: Long) { | ||||||
|         uiState.value = uiState.value.copy(color = newValue) |         uiState.value = uiState.value.copy(color = newValue) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     fun onDelete(openAndPopUp: (String, String) -> Unit) { |  | ||||||
|         subjectDAO.deleteSubject(selectedSubject()) |  | ||||||
|         openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @HiltViewModel | ||||||
|  | class SubjectCreateFormViewModel @Inject constructor( | ||||||
|  |     subjectDAO: SubjectDAO, | ||||||
|  |     selectedSubject: SelectedSubject, | ||||||
|  |     logService: LogService, | ||||||
|  | ) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) { | ||||||
|  |     override val uiState = mutableStateOf(SubjectFormUiState()) | ||||||
|  | 
 | ||||||
|     fun onCreate(openAndPopUp: (String, String) -> Unit) { |     fun onCreate(openAndPopUp: (String, String) -> Unit) { | ||||||
|         val newSubject = Subject( |         val newSubject = Subject( | ||||||
|             name = name, |             name = name, | ||||||
|  | @ -57,6 +54,25 @@ class SubjectFormViewModel @Inject constructor( | ||||||
| //        open(StudeezDestinations.TASKS_SCREEN) | //        open(StudeezDestinations.TASKS_SCREEN) | ||||||
|         openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.ADD_SUBJECT_FORM) |         openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.ADD_SUBJECT_FORM) | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @HiltViewModel | ||||||
|  | class SubjectEditFormViewModel @Inject constructor( | ||||||
|  |     subjectDAO: SubjectDAO, | ||||||
|  |     selectedSubject: SelectedSubject, | ||||||
|  |     logService: LogService, | ||||||
|  | ) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) { | ||||||
|  |     override val uiState = mutableStateOf( | ||||||
|  |         SubjectFormUiState( | ||||||
|  |             name = selectedSubject().name, | ||||||
|  |             color = selectedSubject().argb_color | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     fun onDelete(openAndPopUp: (String, String) -> Unit) { | ||||||
|  |         subjectDAO.updateSubject(selectedSubject().copy(archived = true)) | ||||||
|  |         openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     fun onEdit(openAndPopUp: (String, String) -> Unit) { |     fun onEdit(openAndPopUp: (String, String) -> Unit) { | ||||||
|         val newSubject = selectedSubject().copy( |         val newSubject = selectedSubject().copy( | ||||||
|  | @ -1,80 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks |  | ||||||
| 
 |  | ||||||
| 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.runtime.Composable |  | ||||||
| import androidx.compose.runtime.collectAsState |  | ||||||
| import androidx.compose.ui.Modifier |  | ||||||
| import androidx.compose.ui.res.stringResource |  | ||||||
| import androidx.compose.ui.tooling.preview.Preview |  | ||||||
| import androidx.compose.ui.unit.dp |  | ||||||
| import be.ugent.sel.studeez.common.composable.NewTaskSubjectButton |  | ||||||
| import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate |  | ||||||
| import be.ugent.sel.studeez.common.composable.drawer.DrawerActions |  | ||||||
| import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions |  | ||||||
| import be.ugent.sel.studeez.common.composable.tasks.SubjectEntry |  | ||||||
| import be.ugent.sel.studeez.data.local.models.task.Subject |  | ||||||
| import kotlinx.coroutines.flow.Flow |  | ||||||
| import kotlinx.coroutines.flow.flowOf |  | ||||||
| import be.ugent.sel.studeez.R.string as AppText |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun SubjectRoute( |  | ||||||
|     open: (String) -> Unit, |  | ||||||
|     viewModel: SubjectViewModel, |  | ||||||
|     drawerActions: DrawerActions, |  | ||||||
|     navigationBarActions: NavigationBarActions, |  | ||||||
| ) { |  | ||||||
|     SubjectScreen( |  | ||||||
|         drawerActions = drawerActions, |  | ||||||
|         navigationBarActions = navigationBarActions, |  | ||||||
|         addSubject = { viewModel.addSubject(open) }, |  | ||||||
|         getSubjects = viewModel::getSubjects, |  | ||||||
|         onViewSubject = { viewModel.onViewSubject(it, open) }, |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Composable |  | ||||||
| fun SubjectScreen( |  | ||||||
|     drawerActions: DrawerActions, |  | ||||||
|     navigationBarActions: NavigationBarActions, |  | ||||||
|     addSubject: () -> Unit, |  | ||||||
|     getSubjects: () -> Flow<List<Subject>>, |  | ||||||
|     onViewSubject: (Subject) -> Unit, |  | ||||||
| ) { |  | ||||||
|     PrimaryScreenTemplate( |  | ||||||
|         title = stringResource(AppText.my_subjects), |  | ||||||
|         drawerActions = drawerActions, |  | ||||||
|         navigationBarActions = navigationBarActions, |  | ||||||
|         barAction = {}, |  | ||||||
|     ) { |  | ||||||
|         val subjects = getSubjects().collectAsState(initial = emptyList()) |  | ||||||
|         Column( |  | ||||||
|             modifier = Modifier.padding(top = 5.dp) |  | ||||||
|         ) { |  | ||||||
|             LazyColumn { |  | ||||||
|                 items(subjects.value) { |  | ||||||
|                     SubjectEntry( |  | ||||||
|                         subject = it, |  | ||||||
|                         onViewSubject = { onViewSubject(it) }, |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             NewTaskSubjectButton(onClick = addSubject, AppText.new_subject) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Preview |  | ||||||
| @Composable |  | ||||||
| fun SubjectScreenPreview() { |  | ||||||
|     SubjectScreen( |  | ||||||
|         drawerActions = DrawerActions({}, {}, {}, {}, {}), |  | ||||||
|         navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), |  | ||||||
|         addSubject = {}, |  | ||||||
|         getSubjects = { flowOf() }, |  | ||||||
|         onViewSubject = {}, |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  | @ -27,10 +27,10 @@ data class TaskActions( | ||||||
|     val addTask: () -> Unit, |     val addTask: () -> Unit, | ||||||
|     val getSubject: () -> Subject, |     val getSubject: () -> Subject, | ||||||
|     val getTasks: () -> Flow<List<Task>>, |     val getTasks: () -> Flow<List<Task>>, | ||||||
|     val deleteTask: (Task) -> Unit, |  | ||||||
|     val onCheckTask: (Task, Boolean) -> Unit, |     val onCheckTask: (Task, Boolean) -> Unit, | ||||||
|     val editSubject: () -> Unit, |     val editSubject: () -> Unit, | ||||||
|     val startTask: (Task) -> Unit |     val startTask: (Task) -> Unit, | ||||||
|  |     val archiveTask: (Task) -> Unit, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions { | fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions { | ||||||
|  | @ -38,10 +38,10 @@ fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskAction | ||||||
|         addTask = { viewModel.addTask(open) }, |         addTask = { viewModel.addTask(open) }, | ||||||
|         getTasks = viewModel::getTasks, |         getTasks = viewModel::getTasks, | ||||||
|         getSubject = viewModel::getSelectedSubject, |         getSubject = viewModel::getSelectedSubject, | ||||||
|         deleteTask = viewModel::deleteTask, |  | ||||||
|         onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) }, |         onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) }, | ||||||
|         editSubject = { viewModel.editSubject(open) }, |         editSubject = { viewModel.editSubject(open) }, | ||||||
|         startTask = { task -> viewModel.startTask(task, open) } |         startTask = { task -> viewModel.startTask(task, open) }, | ||||||
|  |         archiveTask = viewModel::archiveTask | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -76,7 +76,7 @@ fun TaskScreen( | ||||||
|                     TaskEntry( |                     TaskEntry( | ||||||
|                         task = it, |                         task = it, | ||||||
|                         onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, |                         onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, | ||||||
|                         onDeleteTask = { taskActions.deleteTask(it) }, |                         onArchiveTask = { taskActions.archiveTask(it) }, | ||||||
|                         onStartTask = { taskActions.startTask(it) } |                         onStartTask = { taskActions.startTask(it) } | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|  | @ -108,10 +108,10 @@ fun TaskScreenPreview() { | ||||||
|             {}, |             {}, | ||||||
|             { Subject(name = "Test Subject") }, |             { Subject(name = "Test Subject") }, | ||||||
|             { flowOf() }, |             { flowOf() }, | ||||||
|             {}, |  | ||||||
|             { _, _ -> run {} }, |             { _, _ -> run {} }, | ||||||
|             {}, |             {}, | ||||||
|             {} |             {}, | ||||||
|  |             {}, | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  | @ -5,7 +5,6 @@ import be.ugent.sel.studeez.data.SelectedTask | ||||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | 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.data.local.models.task.Task | ||||||
| import be.ugent.sel.studeez.domain.LogService | import be.ugent.sel.studeez.domain.LogService | ||||||
| import be.ugent.sel.studeez.domain.SubjectDAO |  | ||||||
| import be.ugent.sel.studeez.domain.TaskDAO | import be.ugent.sel.studeez.domain.TaskDAO | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel | import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
|  | @ -16,7 +15,6 @@ import javax.inject.Inject | ||||||
| @HiltViewModel | @HiltViewModel | ||||||
| class TaskViewModel @Inject constructor( | class TaskViewModel @Inject constructor( | ||||||
|     private val taskDAO: TaskDAO, |     private val taskDAO: TaskDAO, | ||||||
|     private val subjectDAO: SubjectDAO, |  | ||||||
|     private val selectedSubject: SelectedSubject, |     private val selectedSubject: SelectedSubject, | ||||||
|     private val selectedTask: SelectedTask, |     private val selectedTask: SelectedTask, | ||||||
|     logService: LogService, |     logService: LogService, | ||||||
|  | @ -29,11 +27,6 @@ class TaskViewModel @Inject constructor( | ||||||
|         return taskDAO.getTasks(selectedSubject()) |         return taskDAO.getTasks(selectedSubject()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun deleteSubject(open: (String) -> Unit) { |  | ||||||
|         subjectDAO.deleteSubject(selectedSubject()) |  | ||||||
|         open(StudeezDestinations.SUBJECT_SCREEN) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fun getSelectedSubject(): Subject { |     fun getSelectedSubject(): Subject { | ||||||
|         return selectedSubject() |         return selectedSubject() | ||||||
|     } |     } | ||||||
|  | @ -42,8 +35,12 @@ class TaskViewModel @Inject constructor( | ||||||
|         taskDAO.deleteTask(task) |         taskDAO.deleteTask(task) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fun archiveTask(task: Task) { | ||||||
|  |         taskDAO.updateTask(task.copy(archived = true)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fun toggleTaskCompleted(task: Task, completed: Boolean) { |     fun toggleTaskCompleted(task: Task, completed: Boolean) { | ||||||
|         taskDAO.toggleTaskCompleted(task, completed) |         taskDAO.updateTask(task.copy(completed = completed)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun editSubject(open: (String) -> Unit) { |     fun editSubject(open: (String) -> Unit) { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks.forms | package be.ugent.sel.studeez.screens.tasks.form | ||||||
| 
 | 
 | ||||||
| import androidx.annotation.StringRes | import androidx.annotation.StringRes | ||||||
| import androidx.compose.foundation.layout.Column | import androidx.compose.foundation.layout.Column | ||||||
|  | @ -18,10 +18,10 @@ import be.ugent.sel.studeez.resources | ||||||
| import be.ugent.sel.studeez.R.string as AppText | import be.ugent.sel.studeez.R.string as AppText | ||||||
| 
 | 
 | ||||||
| @Composable | @Composable | ||||||
| fun TaskAddRoute( | fun TaskCreateRoute( | ||||||
|     goBack: () -> Unit, |     goBack: () -> Unit, | ||||||
|     openAndPopUp: (String, String) -> Unit, |     openAndPopUp: (String, String) -> Unit, | ||||||
|     viewModel: TaskFormViewModel, |     viewModel: TaskCreateFormViewModel, | ||||||
| ) { | ) { | ||||||
|     val uiState by viewModel.uiState |     val uiState by viewModel.uiState | ||||||
|     TaskForm( |     TaskForm( | ||||||
|  | @ -37,7 +37,7 @@ fun TaskAddRoute( | ||||||
| fun TaskEditRoute( | fun TaskEditRoute( | ||||||
|     goBack: () -> Unit, |     goBack: () -> Unit, | ||||||
|     openAndPopUp: (String, String) -> Unit, |     openAndPopUp: (String, String) -> Unit, | ||||||
|     viewModel: TaskFormViewModel, |     viewModel: TaskEditFormViewModel, | ||||||
| ) { | ) { | ||||||
|     val uiState by viewModel.uiState |     val uiState by viewModel.uiState | ||||||
|     TaskForm( |     TaskForm( | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks.forms | package be.ugent.sel.studeez.screens.tasks.form | ||||||
| 
 | 
 | ||||||
| data class TaskFormUiState( | data class TaskFormUiState( | ||||||
|     val name: String = "", |     val name: String = "", | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks.forms | package be.ugent.sel.studeez.screens.tasks.form | ||||||
| 
 | 
 | ||||||
|  | import androidx.compose.runtime.MutableState | ||||||
| import androidx.compose.runtime.mutableStateOf | import androidx.compose.runtime.mutableStateOf | ||||||
| import be.ugent.sel.studeez.data.SelectedSubject | import be.ugent.sel.studeez.data.SelectedSubject | ||||||
| import be.ugent.sel.studeez.data.SelectedTask | import be.ugent.sel.studeez.data.SelectedTask | ||||||
|  | @ -11,38 +12,54 @@ import be.ugent.sel.studeez.screens.StudeezViewModel | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel | import dagger.hilt.android.lifecycle.HiltViewModel | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| @HiltViewModel | abstract class TaskFormViewModel( | ||||||
| class TaskFormViewModel @Inject constructor( |     protected val taskDAO: TaskDAO, | ||||||
|     private val taskDAO: TaskDAO, |     protected val selectedSubject: SelectedSubject, | ||||||
|     private val selectedSubject: SelectedSubject, |     protected val selectedTask: SelectedTask, | ||||||
|     private val selectedTask: SelectedTask, |  | ||||||
|     logService: LogService, |     logService: LogService, | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
|     var uiState = mutableStateOf( |     abstract val uiState: MutableState<TaskFormUiState> | ||||||
|         if (selectedTask.isSet()) TaskFormUiState(selectedTask().name) else TaskFormUiState() |  | ||||||
|     ) |  | ||||||
|         private set |  | ||||||
| 
 | 
 | ||||||
|     private val name: String |     protected val name: String | ||||||
|         get() = uiState.value.name |         get() = uiState.value.name | ||||||
| 
 | 
 | ||||||
|     fun onNameChange(newValue: String) { |     fun onNameChange(newValue: String) { | ||||||
|         uiState.value = uiState.value.copy(name = newValue) |         uiState.value = uiState.value.copy(name = newValue) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     fun onDelete(openAndPopUp: (String, String) -> Unit) { |  | ||||||
|         taskDAO.deleteTask(selectedTask()) |  | ||||||
|         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @HiltViewModel | ||||||
|  | class TaskCreateFormViewModel @Inject constructor( | ||||||
|  |     taskDAO: TaskDAO, | ||||||
|  |     selectedSubject: SelectedSubject, | ||||||
|  |     selectedTask: SelectedTask, | ||||||
|  |     logService: LogService, | ||||||
|  | ) : TaskFormViewModel(taskDAO, selectedSubject, selectedTask, logService) { | ||||||
|  |     override val uiState = mutableStateOf(TaskFormUiState()) | ||||||
|  | 
 | ||||||
|     fun onCreate(openAndPopUp: (String, String) -> Unit) { |     fun onCreate(openAndPopUp: (String, String) -> Unit) { | ||||||
|         val newTask = Task(name = name, subjectId = selectedSubject().id) |         val newTask = Task(name = name, subjectId = selectedSubject().id) | ||||||
|         taskDAO.saveTask(newTask) |         taskDAO.saveTask(newTask) | ||||||
|         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.ADD_TASK_FORM) |         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.ADD_TASK_FORM) | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @HiltViewModel | ||||||
|  | class TaskEditFormViewModel @Inject constructor( | ||||||
|  |     taskDAO: TaskDAO, | ||||||
|  |     selectedSubject: SelectedSubject, | ||||||
|  |     selectedTask: SelectedTask, | ||||||
|  |     logService: LogService, | ||||||
|  | ) : TaskFormViewModel(taskDAO, selectedSubject, selectedTask, logService) { | ||||||
|  |     override val uiState = mutableStateOf(TaskFormUiState()) | ||||||
|  | 
 | ||||||
|  |     fun onDelete(openAndPopUp: (String, String) -> Unit) { | ||||||
|  |         taskDAO.deleteTask(selectedTask()) | ||||||
|  |         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM) | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     fun onEdit(openAndPopUp: (String, String) -> Unit) { |     fun onEdit(openAndPopUp: (String, String) -> Unit) { | ||||||
|         val newTask = Task(name = name) |         val newTask = selectedTask().copy(name = name) | ||||||
|         taskDAO.updateTask(newTask) |         taskDAO.updateTask(newTask) | ||||||
|         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM) |         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM) | ||||||
|     } |     } | ||||||
		Reference in a new issue
	
	 brreynie
						brreynie