commit
						b53e3cdc5a
					
				
					 41 changed files with 513 additions and 341 deletions
				
			
		|  | @ -2,6 +2,7 @@ package be.ugent.sel.studeez.common.composable | |||
| 
 | ||||
| import androidx.annotation.StringRes | ||||
| import androidx.compose.foundation.BorderStroke | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
|  | @ -20,6 +21,7 @@ import androidx.compose.material.icons.filled.Add | |||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.draw.scale | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
|  | @ -48,6 +50,7 @@ fun BasicButton( | |||
|     modifier: Modifier = Modifier, | ||||
|     colors: ButtonColors = ButtonDefaults.buttonColors(), | ||||
|     border: BorderStroke? = null, | ||||
|     enabled: Boolean = true, | ||||
|     onClick: () -> Unit, | ||||
| ) { | ||||
|     Button( | ||||
|  | @ -56,6 +59,7 @@ fun BasicButton( | |||
|         shape = defaultButtonShape(), | ||||
|         colors = colors, | ||||
|         border = border, | ||||
|         enabled = enabled, | ||||
|     ) { | ||||
|         Text( | ||||
|             text = stringResource(text), | ||||
|  | @ -74,17 +78,22 @@ fun BasicButtonPreview() { | |||
| fun StealthButton( | ||||
|     @StringRes text: Int, | ||||
|     modifier: Modifier = Modifier.card(), | ||||
|     enabled: Boolean = true, | ||||
|     onClick: () -> Unit, | ||||
| ) { | ||||
|     //val clickablemodifier = if (disabled) Modifier.clickable(indication = null) else modifier | ||||
|     val borderColor = if (enabled) MaterialTheme.colors.primary | ||||
|                       else MaterialTheme.colors.onSurface.copy(alpha = 0.3f) | ||||
|     BasicButton( | ||||
|         text = text, | ||||
|         onClick = onClick, | ||||
|         modifier = modifier, | ||||
|         enabled = enabled, | ||||
|         colors = ButtonDefaults.buttonColors( | ||||
|             backgroundColor = MaterialTheme.colors.surface, | ||||
|             contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f) | ||||
|             contentColor = borderColor | ||||
|         ), | ||||
|         border = BorderStroke(3.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)) | ||||
|         border = BorderStroke(2.dp, borderColor) | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,10 +14,10 @@ import androidx.compose.ui.text.font.FontWeight | |||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| 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.StealthButton | ||||
| import be.ugent.sel.studeez.data.local.models.FeedEntry | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds | ||||
| import be.ugent.sel.studeez.R.string as AppText | ||||
| 
 | ||||
| @Composable | ||||
| fun FeedEntry( | ||||
|  | @ -69,14 +69,20 @@ fun FeedEntry( | |||
|                     Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) | ||||
|                 } | ||||
|             } | ||||
|             val buttonText: Int = | ||||
|                 if (feedEntry.isArchived) AppText.deleted else AppText.continue_task | ||||
|             StealthButton( | ||||
|                 text = R.string.continue_task, | ||||
|                 text = buttonText, | ||||
|                 enabled = !feedEntry.isArchived, | ||||
|                 modifier = Modifier | ||||
|                     .padding(start = 10.dp, end = 5.dp) | ||||
|                     .weight(6f) | ||||
|             ) { | ||||
|                 continueWithTask() | ||||
|                 if (!feedEntry.isArchived) { | ||||
|                     continueWithTask() | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -84,6 +90,20 @@ fun FeedEntry( | |||
| @Preview | ||||
| @Composable | ||||
| fun FeedEntryPreview() { | ||||
|     FeedEntry( | ||||
|         continueWithTask = {}, | ||||
|         feedEntry = FeedEntry( | ||||
|             argb_color = 0xFFFFD200, | ||||
|             subJectName = "Test Subject", | ||||
|             taskName = "Test Task", | ||||
|             totalStudyTime = 20, | ||||
|         ) | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun FeedEntryOverflowPreview() { | ||||
|     FeedEntry( | ||||
|         continueWithTask = {}, | ||||
|         feedEntry = FeedEntry( | ||||
|  |  | |||
|  | @ -2,14 +2,16 @@ package be.ugent.sel.studeez.common.composable.feed | |||
| 
 | ||||
| import androidx.lifecycle.viewModelScope | ||||
| 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.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 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 javax.inject.Inject | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,13 +1,7 @@ | |||
| package be.ugent.sel.studeez.common.composable.tasks | ||||
| 
 | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| 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.layout.* | ||||
| import androidx.compose.foundation.shape.CircleShape | ||||
| import androidx.compose.material.Card | ||||
| 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.filled.List | ||||
| 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.draw.clip | ||||
|  | @ -24,16 +20,20 @@ import androidx.compose.ui.text.font.FontWeight | |||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| 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.data.local.models.task.Subject | ||||
| 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 | ||||
| fun SubjectEntry( | ||||
|     subject: Subject, | ||||
|     onViewSubject: () -> Unit, | ||||
|     getStudyTime: () -> Flow<Int>, | ||||
| ) { | ||||
|     val studytime by getStudyTime().collectAsState(initial = 0) | ||||
|     Card( | ||||
|         modifier = Modifier | ||||
|             .fillMaxWidth() | ||||
|  | @ -70,7 +70,7 @@ fun SubjectEntry( | |||
|                         verticalAlignment = Alignment.CenterVertically, | ||||
|                     ) { | ||||
|                         Text( | ||||
|                             text = HoursMinutesSeconds(subject.time).toString(), | ||||
|                             text = HoursMinutesSeconds(studytime).toString(), | ||||
|                         ) | ||||
|                         Row( | ||||
|                             verticalAlignment = Alignment.CenterVertically, | ||||
|  | @ -80,7 +80,7 @@ fun SubjectEntry( | |||
|                                 imageVector = Icons.Default.List, | ||||
|                                 contentDescription = stringResource(id = AppText.tasks) | ||||
|                             ) | ||||
|                             Text(text = "0/0") // TODO | ||||
|                             Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | @ -104,9 +104,12 @@ fun SubjectEntryPreview() { | |||
|         subject = Subject( | ||||
|             name = "Test Subject", | ||||
|             argb_color = 0xFFFFD200, | ||||
|             time = 60 | ||||
|             taskCount = 5, | ||||
|             taskCompletedCount = 2, | ||||
|         ), | ||||
|     ) {} | ||||
|         onViewSubject = {}, | ||||
|         getStudyTime = { flowOf() } | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
|  | @ -116,7 +119,8 @@ fun OverflowSubjectEntryPreview() { | |||
|         subject = Subject( | ||||
|             name = "Testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", | ||||
|             argb_color = 0xFFFFD200, | ||||
|             time = 60 | ||||
|         ), | ||||
|     ) {} | ||||
|         onViewSubject = {}, | ||||
|         getStudyTime = { flowOf() } | ||||
|     ) | ||||
| } | ||||
|  | @ -1,17 +1,7 @@ | |||
| package be.ugent.sel.studeez.common.composable.tasks | ||||
| 
 | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Box | ||||
| 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.foundation.layout.* | ||||
| import androidx.compose.material.* | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Delete | ||||
| import androidx.compose.runtime.Composable | ||||
|  | @ -31,7 +21,7 @@ import be.ugent.sel.studeez.resources | |||
| fun TaskEntry( | ||||
|     task: Task, | ||||
|     onCheckTask: (Boolean) -> Unit, | ||||
|     onDeleteTask: () -> Unit, | ||||
|     onArchiveTask: () -> Unit, | ||||
|     onStartTask: () -> Unit | ||||
| ) { | ||||
|     Card( | ||||
|  | @ -81,7 +71,7 @@ fun TaskEntry( | |||
|             Box(modifier = Modifier.weight(7f)) { | ||||
|                 if (task.completed) { | ||||
|                     IconButton( | ||||
|                         onClick = onDeleteTask, | ||||
|                         onClick = onArchiveTask, | ||||
|                         modifier = Modifier | ||||
|                             .padding(start = 20.dp) | ||||
|                     ) { | ||||
|  |  | |||
|  | @ -1,8 +1,12 @@ | |||
| package be.ugent.sel.studeez.common.ext | ||||
| 
 | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.interaction.MutableInteractionSource | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.composed | ||||
| import androidx.compose.ui.unit.dp | ||||
| 
 | ||||
| fun Modifier.textButton(): Modifier { | ||||
|  |  | |||
|  | @ -1,11 +0,0 @@ | |||
| package be.ugent.sel.studeez.data | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| @Singleton | ||||
| class EditTimerState @Inject constructor(){ | ||||
|     lateinit var timerInfo: TimerInfo | ||||
| } | ||||
							
								
								
									
										45
									
								
								app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| package be.ugent.sel.studeez.data | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.local.models.SessionReport | ||||
| 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.timer_functional.FunctionalTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| /** | ||||
|  * Used to cummunicate between viewmodels. | ||||
|  */ | ||||
| abstract class SelectedState<T> { | ||||
|     abstract var value: T | ||||
|     operator fun invoke() = value | ||||
|     fun set(newValue: T) { | ||||
|         this.value = newValue | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Singleton | ||||
| class SelectedSessionReport @Inject constructor() : SelectedState<SessionReport>() { | ||||
|     override lateinit var value: SessionReport | ||||
| } | ||||
| 
 | ||||
| @Singleton | ||||
| class SelectedTask @Inject constructor() : SelectedState<Task>() { | ||||
|     override lateinit var value: Task | ||||
| } | ||||
| 
 | ||||
| @Singleton | ||||
| class SelectedTimer @Inject constructor() : SelectedState<FunctionalTimer>() { | ||||
|     override lateinit var value: FunctionalTimer | ||||
| } | ||||
| 
 | ||||
| @Singleton | ||||
| class SelectedSubject @Inject constructor() : SelectedState<Subject>() { | ||||
|     override lateinit var value: Subject | ||||
| } | ||||
| 
 | ||||
| @Singleton | ||||
| class SelectedTimerInfo @Inject constructor() : SelectedState<TimerInfo>() { | ||||
|     override lateinit var value: TimerInfo | ||||
| } | ||||
|  | @ -1,20 +0,0 @@ | |||
| package be.ugent.sel.studeez.data | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| /** | ||||
|  * 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 | ||||
| class SelectedSubject @Inject constructor() { | ||||
|     private lateinit var subject: Subject | ||||
|     operator fun invoke() = subject | ||||
|     fun set(subject: Subject) { | ||||
|         this.subject = subject | ||||
|     } | ||||
| 
 | ||||
|     fun isSet() = this::subject.isInitialized | ||||
| } | ||||
|  | @ -1,21 +0,0 @@ | |||
| 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 | ||||
| } | ||||
|  | @ -1,14 +0,0 @@ | |||
| package be.ugent.sel.studeez.data | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| /** | ||||
|  * Used to communicate the SelectedTimer from the selection screen to the session screen. | ||||
|  * Because this is a singleton-class the view-models of both screens observe the same data. | ||||
|  */ | ||||
| @Singleton | ||||
| class SelectedTimerState @Inject constructor(){ | ||||
|     var selectedTimer: FunctionalTimer? = null | ||||
| } | ||||
|  | @ -1,14 +0,0 @@ | |||
| package be.ugent.sel.studeez.data | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.local.models.SessionReport | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| /** | ||||
|  * Used to communicate the SelectedTimer from the selection screen to the session screen. | ||||
|  * Because this is a singleton-class the view-models of both screens observe the same data. | ||||
|  */ | ||||
| @Singleton | ||||
| class SessionReportState @Inject constructor(){ | ||||
|     var sessionReport: SessionReport? = null | ||||
| } | ||||
|  | @ -9,5 +9,6 @@ data class FeedEntry( | |||
|     val taskId: String = "", // Name of task is not unique | ||||
|     val subjectId: String = "", | ||||
|     val totalStudyTime: Int = 0, | ||||
|     val endTime: Timestamp = Timestamp(0, 0) | ||||
|     val endTime: Timestamp = Timestamp(0, 0), | ||||
|     val isArchived: Boolean = false | ||||
| ) | ||||
|  |  | |||
|  | @ -1,10 +1,22 @@ | |||
| package be.ugent.sel.studeez.data.local.models.task | ||||
| 
 | ||||
| import com.google.firebase.firestore.DocumentId | ||||
| import com.google.firebase.firestore.Exclude | ||||
| 
 | ||||
| data class Subject( | ||||
|     @DocumentId val id: String = "", | ||||
|     val name: String = "", | ||||
|     val time: Int = 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,9 +5,10 @@ import com.google.firebase.firestore.DocumentId | |||
| data class Task( | ||||
|     @DocumentId val id: String = "", | ||||
|     val name: String = "", | ||||
|     val completed: Boolean = false, | ||||
|     var completed: Boolean = false, | ||||
|     val time: Int = 0, | ||||
|     val subjectId: String = "", | ||||
|     var archived: Boolean = false, | ||||
| ) | ||||
| 
 | ||||
| object TaskDocument { | ||||
|  | @ -16,4 +17,5 @@ object TaskDocument { | |||
|     const val completed = "completed" | ||||
|     const val time = "time" | ||||
|     const val subjectId = "subjectId" | ||||
|     const val archived = "archived" | ||||
| } | ||||
|  |  | |||
|  | @ -12,5 +12,10 @@ interface SubjectDAO { | |||
|     fun deleteSubject(oldSubject: 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? | ||||
| } | ||||
|  | @ -14,7 +14,5 @@ interface TaskDAO { | |||
| 
 | ||||
|     fun deleteTask(oldTask: Task) | ||||
| 
 | ||||
|     fun toggleTaskCompleted(task: Task, completed: Boolean) | ||||
| 
 | ||||
|     suspend fun getTask(subjectId: String, taskId: String): Task | ||||
| } | ||||
|  | @ -1,11 +1,14 @@ | |||
| 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.Task | ||||
| import be.ugent.sel.studeez.data.local.models.task.SubjectDocument | ||||
| import be.ugent.sel.studeez.domain.AccountDAO | ||||
| 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.FirebaseFirestore | ||||
| import com.google.firebase.firestore.Query | ||||
| import com.google.firebase.firestore.ktx.snapshots | ||||
| import com.google.firebase.firestore.ktx.toObject | ||||
| import kotlinx.coroutines.flow.Flow | ||||
|  | @ -16,11 +19,20 @@ import javax.inject.Inject | |||
| class FireBaseSubjectDAO @Inject constructor( | ||||
|     private val firestore: FirebaseFirestore, | ||||
|     private val auth: AccountDAO, | ||||
|     private val taskDAO: TaskDAO, | ||||
| ) : SubjectDAO { | ||||
|     override fun getSubjects(): Flow<List<Subject>> { | ||||
|         return currentUserSubjectsCollection() | ||||
|             .subjectNotArchived() | ||||
|             .snapshots() | ||||
|             .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? { | ||||
|  | @ -39,8 +51,45 @@ class FireBaseSubjectDAO @Inject constructor( | |||
|         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 = | ||||
|         firestore.collection(FireBaseCollections.USER_COLLECTION) | ||||
|             .document(auth.currentUserId) | ||||
|             .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 com.google.firebase.firestore.CollectionReference | ||||
| 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.toObject | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.flow | ||||
| import kotlinx.coroutines.flow.map | ||||
| import kotlinx.coroutines.tasks.await | ||||
| import javax.inject.Inject | ||||
|  | @ -21,6 +21,7 @@ class FireBaseTaskDAO @Inject constructor( | |||
| ) : TaskDAO { | ||||
|     override fun getTasks(subject: Subject): Flow<List<Task>> { | ||||
|         return selectedSubjectTasksCollection(subject.id) | ||||
|             .taskNotArchived() | ||||
|             .snapshots() | ||||
|             .map { it.toObjects(Task::class.java) } | ||||
|     } | ||||
|  | @ -34,23 +35,34 @@ class FireBaseTaskDAO @Inject constructor( | |||
|     } | ||||
| 
 | ||||
|     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) { | ||||
|         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 = | ||||
|         firestore.collection(FireBaseCollections.USER_COLLECTION) | ||||
|             .document(auth.currentUserId) | ||||
|             .collection(FireBaseCollections.SUBJECT_COLLECTION) | ||||
|             .document(subjectId) | ||||
|             .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,30 +7,32 @@ 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.domain.FeedDAO | ||||
| import be.ugent.sel.studeez.domain.SessionDAO | ||||
| import be.ugent.sel.studeez.domain.SubjectDAO | ||||
| import be.ugent.sel.studeez.domain.TaskDAO | ||||
| import com.google.firebase.Timestamp | ||||
| import kotlinx.coroutines.flow.* | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.map | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| class FirebaseFeedDAO @Inject constructor( | ||||
|     private val sessionDAO: SessionDAO, | ||||
|     private val taskDAO: TaskDAO, | ||||
|     private val subjectDAO: FireBaseSubjectDAO | ||||
|     private val subjectDAO: SubjectDAO | ||||
| ) : FeedDAO { | ||||
| 
 | ||||
|     /** | ||||
|      *  Return a map as with key the day and value a list of feedentries for that day. | ||||
|      */ | ||||
|     override fun getFeedEntries(): Flow<Map<String, List<FeedEntry>>> { | ||||
|         return sessionDAO.getSessions().map {sessionReports -> | ||||
|         return sessionDAO.getSessions().map { sessionReports -> | ||||
|             sessionReports | ||||
|                 .map { sessionReport ->  sessionToFeedEntry(sessionReport) } | ||||
|                 .map { sessionReport -> sessionToFeedEntry(sessionReport) } | ||||
|                 .sortedByDescending { it.endTime } | ||||
|                 .groupBy { getFormattedTime(it) } | ||||
|                 .mapValues { (_, entries) -> | ||||
|                     entries | ||||
|                     .groupBy { it.taskId } | ||||
|                     .map { fuseFeedEntries(it.component2()) } | ||||
|                         .groupBy { it.taskId } | ||||
|                         .map { fuseFeedEntries(it.component2()) } | ||||
|                 } | ||||
|         } | ||||
|     } | ||||
|  | @ -72,7 +74,8 @@ class FirebaseFeedDAO @Inject constructor( | |||
|             taskId = task.id, | ||||
|             subjectId = subject.id, | ||||
|             totalStudyTime = sessionReport.studyTime, | ||||
|             endTime = sessionReport.endTime | ||||
|             endTime = sessionReport.endTime, | ||||
|             isArchived = task.archived || subject.archived | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | @ -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.sign_up.SignUpRoute | ||||
| 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.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.subjects.form.SubjectCreateRoute | ||||
| import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute | ||||
| import be.ugent.sel.studeez.screens.tasks.form.TaskCreateRoute | ||||
| 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.TimerEditRoute | ||||
| import be.ugent.sel.studeez.screens.timer_form.timer_type_select.TimerTypeSelectScreen | ||||
|  | @ -81,7 +81,7 @@ fun StudeezNavGraph( | |||
|         } | ||||
| 
 | ||||
|         composable(StudeezDestinations.ADD_SUBJECT_FORM) { | ||||
|             SubjectAddRoute( | ||||
|             SubjectCreateRoute( | ||||
|                 goBack = goBack, | ||||
|                 openAndPopUp = openAndPopUp, | ||||
|                 viewModel = hiltViewModel(), | ||||
|  | @ -105,7 +105,7 @@ fun StudeezNavGraph( | |||
|         } | ||||
| 
 | ||||
|         composable(StudeezDestinations.ADD_TASK_FORM) { | ||||
|             TaskAddRoute( | ||||
|             TaskCreateRoute( | ||||
|                 goBack = goBack, | ||||
|                 openAndPopUp = openAndPopUp, | ||||
|                 viewModel = hiltViewModel(), | ||||
|  |  | |||
|  | @ -1,9 +1,8 @@ | |||
| package be.ugent.sel.studeez.screens.session | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.SelectedSessionReport | ||||
| import be.ugent.sel.studeez.data.SelectedTask | ||||
| import be.ugent.sel.studeez.data.SelectedTimerState | ||||
| import be.ugent.sel.studeez.data.SessionReportState | ||||
| import be.ugent.sel.studeez.data.local.models.task.Task | ||||
| import be.ugent.sel.studeez.data.SelectedTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer | ||||
| import be.ugent.sel.studeez.domain.LogService | ||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||
|  | @ -13,24 +12,21 @@ import javax.inject.Inject | |||
| 
 | ||||
| @HiltViewModel | ||||
| class SessionViewModel @Inject constructor( | ||||
|     private val selectedTimerState: SelectedTimerState, | ||||
|     private val sessionReportState: SessionReportState, | ||||
|     private val selectedTimer: SelectedTimer, | ||||
|     private val sessionReport: SelectedSessionReport, | ||||
|     private val selectedTask: SelectedTask, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
|     private val task : Task = selectedTask() | ||||
| 
 | ||||
|     fun getTimer() : FunctionalTimer { | ||||
|         return selectedTimerState.selectedTimer!! | ||||
|     fun getTimer(): FunctionalTimer { | ||||
|         return selectedTimer() | ||||
|     } | ||||
| 
 | ||||
|     fun getTask(): String { | ||||
|         return task.name | ||||
|         return selectedTask().name | ||||
|     } | ||||
| 
 | ||||
|     fun endSession(openAndPopUp: (String, String) -> Unit) { | ||||
|         sessionReportState.sessionReport = getTimer().getSessionReport(task.subjectId, task.id) | ||||
|         sessionReport.set(getTimer().getSessionReport(selectedTask().subjectId, selectedTask().id)) | ||||
|         openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN) | ||||
|     } | ||||
| } | ||||
|  | @ -1,9 +1,11 @@ | |||
| package be.ugent.sel.studeez.screens.session_recap | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.SessionReportState | ||||
| import be.ugent.sel.studeez.data.SelectedSessionReport | ||||
| import be.ugent.sel.studeez.data.SelectedTask | ||||
| import be.ugent.sel.studeez.data.local.models.SessionReport | ||||
| import be.ugent.sel.studeez.domain.LogService | ||||
| 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.screens.StudeezViewModel | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
|  | @ -11,19 +13,22 @@ import javax.inject.Inject | |||
| 
 | ||||
| @HiltViewModel | ||||
| class SessionRecapViewModel @Inject constructor( | ||||
|     sessionReportState: SessionReportState, | ||||
|     private val selectedSessionReport: SelectedSessionReport, | ||||
|     private val sessionDAO: SessionDAO, | ||||
|     private val taskDAO: TaskDAO, | ||||
|     private val selectedTask: SelectedTask, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
|     private val report: SessionReport = sessionReportState.sessionReport!! | ||||
| 
 | ||||
|     fun getSessionReport(): SessionReport { | ||||
|         return report | ||||
|         return selectedSessionReport() | ||||
|     } | ||||
| 
 | ||||
|     fun saveSession(open: (String, String) -> Unit) { | ||||
|         sessionDAO.saveSession(getSessionReport()) | ||||
|         val newTask = | ||||
|             selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime) | ||||
|         taskDAO.updateTask(newTask) | ||||
|         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.local.models.task.Subject | ||||
| 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.screens.StudeezViewModel | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.* | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| @HiltViewModel | ||||
|  | @ -16,12 +17,21 @@ class SubjectViewModel @Inject constructor( | |||
|     private val selectedSubject: SelectedSubject, | ||||
|     logService: 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) | ||||
|     } | ||||
| 
 | ||||
|     fun getSubjects(): Flow<List<Subject>> { | ||||
|         return subjectDAO.getSubjects() | ||||
|     fun getStudyTime(subject: Subject): Flow<Int> { | ||||
|         return subjectDAO.getStudyTime(subject) | ||||
|     } | ||||
| 
 | ||||
|     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.compose.foundation.layout.Column | ||||
|  | @ -19,10 +19,10 @@ import be.ugent.sel.studeez.resources | |||
| import be.ugent.sel.studeez.R.string as AppText | ||||
| 
 | ||||
| @Composable | ||||
| fun SubjectAddRoute( | ||||
| fun SubjectCreateRoute( | ||||
|     goBack: () -> Unit, | ||||
|     openAndPopUp: (String, String) -> Unit, | ||||
|     viewModel: SubjectFormViewModel, | ||||
|     viewModel: SubjectCreateFormViewModel, | ||||
| ) { | ||||
|     val uiState by viewModel.uiState | ||||
|     SubjectForm( | ||||
|  | @ -39,7 +39,7 @@ fun SubjectAddRoute( | |||
| fun SubjectEditRoute( | ||||
|     goBack: () -> Unit, | ||||
|     openAndPopUp: (String, String) -> Unit, | ||||
|     viewModel: SubjectFormViewModel, | ||||
|     viewModel: SubjectEditFormViewModel, | ||||
| ) { | ||||
|     val uiState by viewModel.uiState | ||||
|     SubjectForm( | ||||
|  | @ -1,4 +1,4 @@ | |||
| package be.ugent.sel.studeez.screens.tasks.forms | ||||
| package be.ugent.sel.studeez.screens.subjects.form | ||||
| 
 | ||||
| data class SubjectFormUiState( | ||||
|     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 be.ugent.sel.studeez.data.SelectedSubject | ||||
| 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 javax.inject.Inject | ||||
| 
 | ||||
| @HiltViewModel | ||||
| class SubjectFormViewModel @Inject constructor( | ||||
|     private val subjectDAO: SubjectDAO, | ||||
|     private val selectedSubject: SelectedSubject, | ||||
| abstract class SubjectFormViewModel( | ||||
|     protected val subjectDAO: SubjectDAO, | ||||
|     protected val selectedSubject: SelectedSubject, | ||||
|     logService: LogService, | ||||
| ) : StudeezViewModel(logService) { | ||||
|     var uiState = mutableStateOf( | ||||
|         if (selectedSubject.isSet()) SubjectFormUiState( | ||||
|             name = selectedSubject().name, | ||||
|             color = selectedSubject().argb_color | ||||
|         ) | ||||
|         else SubjectFormUiState() | ||||
|     ) | ||||
|         private set | ||||
|     abstract val uiState: MutableState<SubjectFormUiState> | ||||
| 
 | ||||
|     private val name: String | ||||
|     protected val name: String | ||||
|         get() = uiState.value.name | ||||
| 
 | ||||
|     private val color: Long | ||||
|     protected val color: Long | ||||
|         get() = uiState.value.color | ||||
| 
 | ||||
|     fun onNameChange(newValue: String) { | ||||
|  | @ -38,11 +31,15 @@ class SubjectFormViewModel @Inject constructor( | |||
|     fun onColorChange(newValue: Long) { | ||||
|         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) { | ||||
|         val newSubject = Subject( | ||||
|  | @ -57,6 +54,25 @@ class SubjectFormViewModel @Inject constructor( | |||
| //        open(StudeezDestinations.TASKS_SCREEN) | ||||
|         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) { | ||||
|         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 getSubject: () -> Subject, | ||||
|     val getTasks: () -> Flow<List<Task>>, | ||||
|     val deleteTask: (Task) -> Unit, | ||||
|     val onCheckTask: (Task, Boolean) -> Unit, | ||||
|     val editSubject: () -> Unit, | ||||
|     val startTask: (Task) -> Unit | ||||
|     val startTask: (Task) -> Unit, | ||||
|     val archiveTask: (Task) -> Unit, | ||||
| ) | ||||
| 
 | ||||
| fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions { | ||||
|  | @ -38,10 +38,10 @@ fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskAction | |||
|         addTask = { viewModel.addTask(open) }, | ||||
|         getTasks = viewModel::getTasks, | ||||
|         getSubject = viewModel::getSelectedSubject, | ||||
|         deleteTask = viewModel::deleteTask, | ||||
|         onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) }, | ||||
|         editSubject = { viewModel.editSubject(open) }, | ||||
|         startTask = { task -> viewModel.startTask(task, open) } | ||||
|         startTask = { task -> viewModel.startTask(task, open) }, | ||||
|         archiveTask = viewModel::archiveTask | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  | @ -71,17 +71,25 @@ fun TaskScreen( | |||
|         Column( | ||||
|             modifier = Modifier.padding(top = 5.dp) | ||||
|         ) { | ||||
|             NewTaskSubjectButton(onClick = taskActions.addTask, AppText.new_task) | ||||
|             LazyColumn { | ||||
|                 items(tasks.value) { | ||||
|                 items(tasks.value.filter { !it.completed }) { | ||||
|                     TaskEntry( | ||||
|                         task = it, | ||||
|                         onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, | ||||
|                         onDeleteTask = { taskActions.deleteTask(it) }, | ||||
|                         onArchiveTask = { taskActions.archiveTask(it) }, | ||||
|                         onStartTask = { taskActions.startTask(it) } | ||||
|                     ) | ||||
|                 } | ||||
|                 items(tasks.value.filter { it.completed }) { | ||||
|                     TaskEntry( | ||||
|                         task = it, | ||||
|                         onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, | ||||
|                         onArchiveTask = { taskActions.archiveTask(it) }, | ||||
|                         onStartTask = { taskActions.startTask(it) } | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|             NewTaskSubjectButton(onClick = taskActions.addTask, AppText.new_task) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -108,10 +116,10 @@ fun TaskScreenPreview() { | |||
|             {}, | ||||
|             { Subject(name = "Test Subject") }, | ||||
|             { flowOf() }, | ||||
|             {}, | ||||
|             { _, _ -> 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.Task | ||||
| 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.navigation.StudeezDestinations | ||||
| import be.ugent.sel.studeez.screens.StudeezViewModel | ||||
|  | @ -16,7 +15,6 @@ import javax.inject.Inject | |||
| @HiltViewModel | ||||
| class TaskViewModel @Inject constructor( | ||||
|     private val taskDAO: TaskDAO, | ||||
|     private val subjectDAO: SubjectDAO, | ||||
|     private val selectedSubject: SelectedSubject, | ||||
|     private val selectedTask: SelectedTask, | ||||
|     logService: LogService, | ||||
|  | @ -29,11 +27,6 @@ class TaskViewModel @Inject constructor( | |||
|         return taskDAO.getTasks(selectedSubject()) | ||||
|     } | ||||
| 
 | ||||
|     fun deleteSubject(open: (String) -> Unit) { | ||||
|         subjectDAO.deleteSubject(selectedSubject()) | ||||
|         open(StudeezDestinations.SUBJECT_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun getSelectedSubject(): Subject { | ||||
|         return selectedSubject() | ||||
|     } | ||||
|  | @ -42,8 +35,12 @@ class TaskViewModel @Inject constructor( | |||
|         taskDAO.deleteTask(task) | ||||
|     } | ||||
| 
 | ||||
|     fun archiveTask(task: Task) { | ||||
|         taskDAO.updateTask(task.copy(archived = true)) | ||||
|     } | ||||
| 
 | ||||
|     fun toggleTaskCompleted(task: Task, completed: Boolean) { | ||||
|         taskDAO.toggleTaskCompleted(task, completed) | ||||
|         taskDAO.updateTask(task.copy(completed = completed)) | ||||
|     } | ||||
| 
 | ||||
|     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.compose.foundation.layout.Column | ||||
|  | @ -18,10 +18,10 @@ import be.ugent.sel.studeez.resources | |||
| import be.ugent.sel.studeez.R.string as AppText | ||||
| 
 | ||||
| @Composable | ||||
| fun TaskAddRoute( | ||||
| fun TaskCreateRoute( | ||||
|     goBack: () -> Unit, | ||||
|     openAndPopUp: (String, String) -> Unit, | ||||
|     viewModel: TaskFormViewModel, | ||||
|     viewModel: TaskCreateFormViewModel, | ||||
| ) { | ||||
|     val uiState by viewModel.uiState | ||||
|     TaskForm( | ||||
|  | @ -37,7 +37,7 @@ fun TaskAddRoute( | |||
| fun TaskEditRoute( | ||||
|     goBack: () -> Unit, | ||||
|     openAndPopUp: (String, String) -> Unit, | ||||
|     viewModel: TaskFormViewModel, | ||||
|     viewModel: TaskEditFormViewModel, | ||||
| ) { | ||||
|     val uiState by viewModel.uiState | ||||
|     TaskForm( | ||||
|  | @ -1,4 +1,4 @@ | |||
| package be.ugent.sel.studeez.screens.tasks.forms | ||||
| package be.ugent.sel.studeez.screens.tasks.form | ||||
| 
 | ||||
| data class TaskFormUiState( | ||||
|     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 be.ugent.sel.studeez.data.SelectedSubject | ||||
| import be.ugent.sel.studeez.data.SelectedTask | ||||
|  | @ -11,39 +12,55 @@ 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, | ||||
| abstract class TaskFormViewModel( | ||||
|     protected val taskDAO: TaskDAO, | ||||
|     protected val selectedSubject: SelectedSubject, | ||||
|     protected val selectedTask: SelectedTask, | ||||
|     logService: LogService, | ||||
| ) : StudeezViewModel(logService) { | ||||
|     var uiState = mutableStateOf( | ||||
|         if (selectedTask.isSet()) TaskFormUiState(selectedTask().name) else TaskFormUiState() | ||||
|     ) | ||||
|         private set | ||||
|     abstract val uiState: MutableState<TaskFormUiState> | ||||
| 
 | ||||
|     private val name: String | ||||
|     protected 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) | ||||
|     } | ||||
| @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) { | ||||
|         val newTask = Task(name = name, subjectId = selectedSubject().id) | ||||
|         taskDAO.saveTask(newTask) | ||||
|         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) { | ||||
|         val newTask = Task(name = name) | ||||
|         val newTask = selectedTask().copy(name = name) | ||||
|         taskDAO.updateTask(newTask) | ||||
|         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM) | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | @ -1,6 +1,6 @@ | |||
| package be.ugent.sel.studeez.screens.timer_form | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.EditTimerState | ||||
| import be.ugent.sel.studeez.data.SelectedTimerInfo | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import be.ugent.sel.studeez.domain.LogService | ||||
| import be.ugent.sel.studeez.domain.TimerDAO | ||||
|  | @ -10,15 +10,12 @@ import javax.inject.Inject | |||
| 
 | ||||
| @HiltViewModel | ||||
| class TimerFormViewModel @Inject constructor( | ||||
|     private val editTimerState: EditTimerState, | ||||
|     private val selectedTimerInfo: SelectedTimerInfo, | ||||
|     private val timerDAO: TimerDAO, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
|     private val timerInfo: TimerInfo = editTimerState.timerInfo | ||||
| 
 | ||||
|     fun getTimerInfo(): TimerInfo { | ||||
|         return timerInfo | ||||
|         return selectedTimerInfo() | ||||
|     } | ||||
| 
 | ||||
|     fun editTimer(timerInfo: TimerInfo, goBack: () -> Unit) { | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| package be.ugent.sel.studeez.screens.timer_form.timer_type_select | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.EditTimerState | ||||
| import be.ugent.sel.studeez.data.SelectedTimerInfo | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import be.ugent.sel.studeez.domain.LogService | ||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||
|  | @ -10,13 +10,13 @@ import javax.inject.Inject | |||
| 
 | ||||
| @HiltViewModel | ||||
| class TimerTypeSelectViewModel @Inject constructor( | ||||
|     private val editTimerState: EditTimerState, | ||||
|     private val selectedTimerInfo: SelectedTimerInfo, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
| 
 | ||||
|     fun onTimerTypeChosen(timerInfo: TimerInfo, open: (String) -> Unit) { | ||||
|         editTimerState.timerInfo = timerInfo | ||||
|         selectedTimerInfo.set(timerInfo) | ||||
|         open(StudeezDestinations.ADD_TIMER_SCREEN) | ||||
|     } | ||||
| } | ||||
|  | @ -1,6 +1,6 @@ | |||
| package be.ugent.sel.studeez.screens.timer_overview | ||||
| 
 | ||||
| import be.ugent.sel.studeez.data.EditTimerState | ||||
| import be.ugent.sel.studeez.data.SelectedTimerInfo | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import be.ugent.sel.studeez.domain.ConfigurationService | ||||
| import be.ugent.sel.studeez.domain.LogService | ||||
|  | @ -15,11 +15,11 @@ import javax.inject.Inject | |||
| class TimerOverviewViewModel @Inject constructor( | ||||
|     private val configurationService: ConfigurationService, | ||||
|     private val timerDAO: TimerDAO, | ||||
|     private val editTimerState: EditTimerState, | ||||
|     private val selectedTimerInfo: SelectedTimerInfo, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
|     fun getUserTimers() : Flow<List<TimerInfo>> { | ||||
|     fun getUserTimers(): Flow<List<TimerInfo>> { | ||||
|         return timerDAO.getUserTimers() | ||||
|     } | ||||
| 
 | ||||
|  | @ -27,8 +27,8 @@ class TimerOverviewViewModel @Inject constructor( | |||
|         return configurationService.getDefaultTimers() | ||||
|     } | ||||
| 
 | ||||
|     fun update(timerInfo: TimerInfo, open: (String) -> Unit)  { | ||||
|         editTimerState.timerInfo = timerInfo | ||||
|     fun update(timerInfo: TimerInfo, open: (String) -> Unit) { | ||||
|         selectedTimerInfo.set(timerInfo) | ||||
|         open(StudeezDestinations.TIMER_EDIT_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|  | @ -36,7 +36,7 @@ class TimerOverviewViewModel @Inject constructor( | |||
|         open(StudeezDestinations.TIMER_TYPE_CHOOSING_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun delete(timerInfo: TimerInfo) =timerDAO.deleteTimer(timerInfo) | ||||
|     fun delete(timerInfo: TimerInfo) = timerDAO.deleteTimer(timerInfo) | ||||
| 
 | ||||
|     fun save(timerInfo: TimerInfo) = timerDAO.saveTimer(timerInfo) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,10 +1,8 @@ | |||
| package be.ugent.sel.studeez.screens.timer_selection | ||||
| 
 | ||||
| import androidx.compose.runtime.MutableState | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import be.ugent.sel.studeez.data.SelectedTimerState | ||||
| import be.ugent.sel.studeez.data.SelectedTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import be.ugent.sel.studeez.domain.LogService | ||||
|  | @ -18,7 +16,7 @@ import javax.inject.Inject | |||
| @HiltViewModel | ||||
| class TimerSelectionViewModel @Inject constructor( | ||||
|     private val timerDAO: TimerDAO, | ||||
|     private val selectedTimerState: SelectedTimerState, | ||||
|     private val selectedTimer: SelectedTimer, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
|  | @ -26,12 +24,12 @@ class TimerSelectionViewModel @Inject constructor( | |||
|         HoursMinutesSeconds(1, 0, 0).getTotalSeconds() | ||||
|     ) | ||||
| 
 | ||||
|     fun getAllTimers() : Flow<List<TimerInfo>> { | ||||
|     fun getAllTimers(): Flow<List<TimerInfo>> { | ||||
|         return timerDAO.getAllTimers() | ||||
|     } | ||||
| 
 | ||||
|     fun startSession(open: (String) -> Unit, timerInfo: TimerInfo) { | ||||
|         selectedTimerState.selectedTimer = timerInfo.getFunctionalTimer() | ||||
|         selectedTimer.set(timerInfo.getFunctionalTimer()) | ||||
|         open(StudeezDestinations.SESSION_SCREEN) | ||||
|     } | ||||
| } | ||||
|  | @ -31,8 +31,9 @@ | |||
| 
 | ||||
|     <!-- Feed--> | ||||
|     <string name="continue_task">Continue</string> | ||||
|     <string name="deleted">Deleted</string> | ||||
|     <string name="your_feed">This is your feed</string> | ||||
|     <string name="empty_feed_help_text">Create you first subject and tasks to get started</string> | ||||
|     <string name="empty_feed_help_text">Click here to create you first subject and tasks to get started</string> | ||||
| 
 | ||||
|     <!-- Tasks --> | ||||
|     <string name="tasks">Tasks</string> | ||||
|  |  | |||
|  | @ -1,11 +1,13 @@ | |||
| package be.ugent.sel.studeez.timer_functional | ||||
| 
 | ||||
| import android.media.MediaPlayer | ||||
| import be.ugent.sel.studeez.data.SelectedTimerState | ||||
| import be.ugent.sel.studeez.data.SessionReportState | ||||
| import be.ugent.sel.studeez.data.SelectedSessionReport | ||||
| import be.ugent.sel.studeez.data.SelectedTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer | ||||
| import be.ugent.sel.studeez.domain.LogService | ||||
| import be.ugent.sel.studeez.domain.implementation.LogServiceImpl | ||||
| import be.ugent.sel.studeez.screens.session.InvisibleSessionManager | ||||
| import be.ugent.sel.studeez.screens.session.SessionViewModel | ||||
| import kotlinx.coroutines.ExperimentalCoroutinesApi | ||||
|  | @ -18,14 +20,14 @@ import org.mockito.kotlin.mock | |||
| 
 | ||||
| @ExperimentalCoroutinesApi | ||||
| class InvisibleSessionManagerTest { | ||||
|     private var timerState: SelectedTimerState = SelectedTimerState() | ||||
|     private var selectedTimer: SelectedTimer = SelectedTimer() | ||||
|     private lateinit var viewModel: SessionViewModel | ||||
|     private var mediaPlayer: MediaPlayer = mock() | ||||
| 
 | ||||
|     @Test | ||||
|     fun InvisibleEndlessTimerTest() = runTest { | ||||
|         timerState.selectedTimer = FunctionalEndlessTimer() | ||||
|         viewModel = SessionViewModel(timerState, SessionReportState(), mock()) | ||||
|         selectedTimer.set(FunctionalEndlessTimer()) | ||||
|         viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) | ||||
|         InvisibleSessionManager.setParameters(viewModel, mediaPlayer) | ||||
| 
 | ||||
|         val test = launch { | ||||
|  | @ -46,8 +48,8 @@ class InvisibleSessionManagerTest { | |||
|         val studyTime = 10 | ||||
|         val breakTime = 5 | ||||
|         val repeats = 1 | ||||
|         timerState.selectedTimer = FunctionalPomodoroTimer(studyTime, breakTime, repeats) | ||||
|         viewModel = SessionViewModel(timerState, SessionReportState(), mock()) | ||||
|         selectedTimer.set(FunctionalPomodoroTimer(studyTime, breakTime, repeats)) | ||||
|         viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) | ||||
|         InvisibleSessionManager.setParameters(viewModel, mediaPlayer) | ||||
| 
 | ||||
|         val test = launch { | ||||
|  | @ -79,8 +81,8 @@ class InvisibleSessionManagerTest { | |||
| 
 | ||||
|     @Test | ||||
|     fun InvisibleCustomTimerTest() = runTest { | ||||
|         timerState.selectedTimer = FunctionalCustomTimer(5) | ||||
|         viewModel = SessionViewModel(timerState, SessionReportState(), mock()) | ||||
|         selectedTimer.set(FunctionalCustomTimer(5)) | ||||
|         viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) | ||||
|         InvisibleSessionManager.setParameters(viewModel, mediaPlayer) | ||||
| 
 | ||||
|         val test = launch { | ||||
|  |  | |||
		Reference in a new issue
	
	 brreynie
						brreynie