commit
						d9d83569db
					
				
					 26 changed files with 549 additions and 51 deletions
				
			
		|  | @ -3,10 +3,13 @@ package be.ugent.sel.studeez.common.composable | ||||||
| import androidx.compose.foundation.layout.Arrangement | import androidx.compose.foundation.layout.Arrangement | ||||||
| import androidx.compose.foundation.layout.Row | import androidx.compose.foundation.layout.Row | ||||||
| import androidx.compose.foundation.layout.fillMaxWidth | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
| import androidx.compose.material.Text | import androidx.compose.material.Text | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
| import androidx.compose.ui.Alignment | import androidx.compose.ui.Alignment | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.text.font.FontWeight | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
| import androidx.compose.ui.unit.sp | import androidx.compose.ui.unit.sp | ||||||
| 
 | 
 | ||||||
| @Composable | @Composable | ||||||
|  | @ -24,3 +27,13 @@ fun Headline( | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun DateText(date: String) { | ||||||
|  |     Text( | ||||||
|  |         text = date, | ||||||
|  |         fontWeight = FontWeight.Bold, | ||||||
|  |         fontSize = 20.sp, | ||||||
|  |         modifier = Modifier.padding(horizontal = 10.dp) | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,160 @@ | ||||||
|  | package be.ugent.sel.studeez.common.composable.feed | ||||||
|  | 
 | ||||||
|  | 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.material.Text | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
|  | import androidx.compose.ui.text.font.FontWeight | ||||||
|  | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
|  | import androidx.compose.ui.unit.sp | ||||||
|  | import be.ugent.sel.studeez.common.composable.BasicTextButton | ||||||
|  | import be.ugent.sel.studeez.common.composable.DateText | ||||||
|  | import be.ugent.sel.studeez.common.composable.Headline | ||||||
|  | import be.ugent.sel.studeez.common.ext.textButton | ||||||
|  | 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 Feed( | ||||||
|  |     uiState: FeedUiState, | ||||||
|  |     continueTask: (String, String) -> Unit, | ||||||
|  |     onEmptyFeedHelp: () -> Unit | ||||||
|  | ) { | ||||||
|  |     when (uiState) { | ||||||
|  |         FeedUiState.Loading -> LoadingFeed() | ||||||
|  |         is FeedUiState.Succes -> LoadedFeed( | ||||||
|  |             uiState = uiState, | ||||||
|  |             continueTask = continueTask, | ||||||
|  |             onEmptyFeedHelp = onEmptyFeedHelp | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun LoadedFeed( | ||||||
|  |     uiState: FeedUiState.Succes, | ||||||
|  |     continueTask: (String, String) -> Unit, | ||||||
|  |     onEmptyFeedHelp: () -> Unit, | ||||||
|  | ) { | ||||||
|  |     if (uiState.feedEntries.isEmpty()) EmptyFeed(onEmptyFeedHelp) | ||||||
|  |     else FeedWithElements(uiState = uiState, continueTask = continueTask) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun LoadingFeed() { | ||||||
|  |     Column( | ||||||
|  |         modifier = Modifier | ||||||
|  |             .fillMaxWidth() | ||||||
|  |             .fillMaxHeight(), | ||||||
|  |         verticalArrangement = Arrangement.Center, | ||||||
|  |         horizontalAlignment = Alignment.CenterHorizontally | ||||||
|  |     ) { | ||||||
|  |         CircularProgressIndicator(color = MaterialTheme.colors.onBackground) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun FeedWithElements( | ||||||
|  |     uiState: FeedUiState.Succes, | ||||||
|  |     continueTask: (String, String) -> Unit, | ||||||
|  | ) { | ||||||
|  |     val feedEntries = uiState.feedEntries | ||||||
|  |     LazyColumn { | ||||||
|  |         items(feedEntries.toList()) { (date, feedEntries) -> | ||||||
|  |             Row( | ||||||
|  |                 horizontalArrangement = Arrangement.SpaceBetween, | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .fillMaxWidth() | ||||||
|  |                     .padding(10.dp), | ||||||
|  |                 verticalAlignment = Alignment.CenterVertically | ||||||
|  |             ) { | ||||||
|  |                 val totalDayStudyTime: Int = feedEntries.sumOf { it.totalStudyTime } | ||||||
|  |                 DateText(date = date) | ||||||
|  |                 Text( | ||||||
|  |                     text = "${HoursMinutesSeconds(totalDayStudyTime)}", | ||||||
|  |                     fontSize = 15.sp, | ||||||
|  |                     fontWeight = FontWeight.Bold | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             feedEntries.forEach { feedEntry -> | ||||||
|  |                 FeedEntry(feedEntry = feedEntry) { | ||||||
|  |                     continueTask(feedEntry.subjectId, feedEntry.taskId) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Spacer(modifier = Modifier.height(20.dp)) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun EmptyFeed(onEmptyFeedHelp: () -> Unit) { | ||||||
|  |     Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { | ||||||
|  |         Column( | ||||||
|  |             horizontalAlignment = Alignment.CenterHorizontally, | ||||||
|  |             modifier = Modifier.fillMaxWidth() | ||||||
|  |         ) { | ||||||
|  |             Headline(text = stringResource(id = AppText.your_feed)) | ||||||
|  | 
 | ||||||
|  |             BasicTextButton( | ||||||
|  |                 AppText.empty_feed_help_text, | ||||||
|  |                 Modifier.textButton(), | ||||||
|  |                 action = onEmptyFeedHelp, | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun FeedLoadingPreview() { | ||||||
|  |     Feed( | ||||||
|  |         uiState = FeedUiState.Loading, | ||||||
|  |         continueTask = { _, _ -> run {} }, {} | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun FeedPreview() { | ||||||
|  |     Feed( | ||||||
|  |         uiState = FeedUiState.Succes( | ||||||
|  |             mapOf( | ||||||
|  |                 "08 May 2023" to listOf( | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFFFD200, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                         totalStudyTime = 600, | ||||||
|  |                     ), | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFFFD200, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                         totalStudyTime = 20, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 "09 May 2023" to listOf( | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFFD1200, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                     ), | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFFFD200, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                     ), | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         ), | ||||||
|  |         continueTask = { _, _ -> run {} }, {} | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,96 @@ | ||||||
|  | package be.ugent.sel.studeez.common.composable.feed | ||||||
|  | 
 | ||||||
|  | import androidx.compose.foundation.background | ||||||
|  | import androidx.compose.foundation.layout.* | ||||||
|  | import androidx.compose.foundation.shape.CircleShape | ||||||
|  | import androidx.compose.material.Card | ||||||
|  | import androidx.compose.material.Text | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.ui.Alignment | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
|  | import androidx.compose.ui.draw.clip | ||||||
|  | import androidx.compose.ui.graphics.Color | ||||||
|  | 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 | ||||||
|  | 
 | ||||||
|  | @Composable | ||||||
|  | fun FeedEntry( | ||||||
|  |     feedEntry: FeedEntry, | ||||||
|  |     continueWithTask: () -> Unit, | ||||||
|  | ) { | ||||||
|  |     Card( | ||||||
|  |         modifier = Modifier | ||||||
|  |             .fillMaxWidth() | ||||||
|  |             .padding(horizontal = 10.dp, vertical = 5.dp), | ||||||
|  |     ) { | ||||||
|  |         Row( | ||||||
|  |             horizontalArrangement = Arrangement.SpaceBetween, | ||||||
|  |             verticalAlignment = Alignment.CenterVertically, | ||||||
|  |         ) { | ||||||
|  |             Row( | ||||||
|  |                 horizontalArrangement = Arrangement.spacedBy(8.dp), | ||||||
|  |                 verticalAlignment = Alignment.CenterVertically, | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .padding(start = 10.dp) | ||||||
|  |                     .weight(11f) | ||||||
|  |             ) { | ||||||
|  |                 Box( | ||||||
|  |                     modifier = Modifier | ||||||
|  |                         .size(20.dp) | ||||||
|  |                         .clip(CircleShape) | ||||||
|  |                         .background(Color(feedEntry.argb_color)), | ||||||
|  |                 ) | ||||||
|  |                 Row( | ||||||
|  |                     modifier = Modifier.fillMaxWidth(), | ||||||
|  |                     horizontalArrangement = Arrangement.SpaceBetween, | ||||||
|  |                     verticalAlignment = Alignment.CenterVertically, | ||||||
|  |                 ) { | ||||||
|  |                     Column( | ||||||
|  |                         verticalArrangement = Arrangement.spacedBy(0.dp) | ||||||
|  |                     ) { | ||||||
|  |                         Text( | ||||||
|  |                             text = feedEntry.subJectName, | ||||||
|  |                             fontWeight = FontWeight.Bold, | ||||||
|  |                             overflow = TextOverflow.Ellipsis, | ||||||
|  |                             maxLines = 1, | ||||||
|  |                         ) | ||||||
|  |                         Text( | ||||||
|  |                             text = feedEntry.taskName, | ||||||
|  |                             overflow = TextOverflow.Ellipsis, | ||||||
|  |                             maxLines = 1, | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                     Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             StealthButton( | ||||||
|  |                 text = R.string.continue_task, | ||||||
|  |                 modifier = Modifier | ||||||
|  |                     .padding(start = 10.dp, end = 5.dp) | ||||||
|  |                     .weight(6f) | ||||||
|  |             ) { | ||||||
|  |                 continueWithTask() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @Preview | ||||||
|  | @Composable | ||||||
|  | fun FeedEntryPreview() { | ||||||
|  |     FeedEntry( | ||||||
|  |         continueWithTask = {}, | ||||||
|  |         feedEntry = FeedEntry( | ||||||
|  |             argb_color = 0xFFFFD200, | ||||||
|  |             subJectName = "Test Subject", | ||||||
|  |             taskName = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkk", | ||||||
|  |             totalStudyTime = 20, | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | package be.ugent.sel.studeez.common.composable.feed | ||||||
|  | 
 | ||||||
|  | import be.ugent.sel.studeez.data.local.models.FeedEntry | ||||||
|  | 
 | ||||||
|  | sealed interface FeedUiState { | ||||||
|  |     object Loading : FeedUiState | ||||||
|  |     data class Succes(val feedEntries: Map<String, List<FeedEntry>>) : FeedUiState | ||||||
|  | } | ||||||
|  | @ -0,0 +1,43 @@ | ||||||
|  | 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.launch | ||||||
|  | import javax.inject.Inject | ||||||
|  | 
 | ||||||
|  | @HiltViewModel | ||||||
|  | class FeedViewModel @Inject constructor( | ||||||
|  |     feedDAO: FeedDAO, | ||||||
|  |     private val taskDAO: TaskDAO, | ||||||
|  |     private val selectedTask: SelectedTask, | ||||||
|  |     logService: LogService | ||||||
|  | ) : StudeezViewModel(logService) { | ||||||
|  | 
 | ||||||
|  |     val uiState: StateFlow<FeedUiState> = feedDAO.getFeedEntries() | ||||||
|  |         .map { FeedUiState.Succes(it) } | ||||||
|  |         .stateIn( | ||||||
|  |             scope = viewModelScope, | ||||||
|  |             initialValue = FeedUiState.Loading, | ||||||
|  |             started = SharingStarted.Eagerly, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     fun continueTask(open: (String) -> Unit, subjectId: String, taskId: String) { | ||||||
|  |         viewModelScope.launch { | ||||||
|  |             val task = taskDAO.getTask(subjectId, taskId) | ||||||
|  |             selectedTask.set(task) | ||||||
|  |             open(StudeezDestinations.TIMER_SELECTION_SCREEN) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun onEmptyFeedHelp(open: (String) -> Unit) { | ||||||
|  |         open(StudeezDestinations.ADD_SUBJECT_FORM) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -32,6 +32,7 @@ fun TaskEntry( | ||||||
|     task: Task, |     task: Task, | ||||||
|     onCheckTask: (Boolean) -> Unit, |     onCheckTask: (Boolean) -> Unit, | ||||||
|     onDeleteTask: () -> Unit, |     onDeleteTask: () -> Unit, | ||||||
|  |     onStartTask: () -> Unit | ||||||
| ) { | ) { | ||||||
|     Card( |     Card( | ||||||
|         modifier = Modifier |         modifier = Modifier | ||||||
|  | @ -95,6 +96,7 @@ fun TaskEntry( | ||||||
|                         modifier = Modifier |                         modifier = Modifier | ||||||
|                             .padding(end = 5.dp), |                             .padding(end = 5.dp), | ||||||
|                     ) { |                     ) { | ||||||
|  |                         onStartTask() | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -110,7 +112,7 @@ fun TaskEntryPreview() { | ||||||
|             name = "Test Task", |             name = "Test Task", | ||||||
|             completed = false, |             completed = false, | ||||||
|         ), |         ), | ||||||
|         {}, {}, |         {}, {}, {} | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -122,7 +124,7 @@ fun CompletedTaskEntryPreview() { | ||||||
|             name = "Test Task", |             name = "Test Task", | ||||||
|             completed = true, |             completed = true, | ||||||
|         ), |         ), | ||||||
|         {}, {}, |         {}, {}, {}, | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -134,6 +136,6 @@ fun OverflowTaskEntryPreview() { | ||||||
|             name = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkkkk", |             name = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkkkk", | ||||||
|             completed = false, |             completed = false, | ||||||
|         ), |         ), | ||||||
|         {}, {}, |         {}, {}, {} | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | package be.ugent.sel.studeez.data.local.models | ||||||
|  | 
 | ||||||
|  | import com.google.firebase.Timestamp | ||||||
|  | 
 | ||||||
|  | data class FeedEntry( | ||||||
|  |     val argb_color: Long = 0, | ||||||
|  |     val subJectName: String = "", | ||||||
|  |     val taskName: String = "", | ||||||
|  |     val taskId: String = "", // Name of task is not unique | ||||||
|  |     val subjectId: String = "", | ||||||
|  |     val totalStudyTime: Int = 0, | ||||||
|  |     val endTime: Timestamp = Timestamp(0, 0) | ||||||
|  | ) | ||||||
|  | @ -6,5 +6,7 @@ import com.google.firebase.firestore.DocumentId | ||||||
| data class SessionReport( | data class SessionReport( | ||||||
|     @DocumentId val id: String = "", |     @DocumentId val id: String = "", | ||||||
|     val studyTime: Int = 0, |     val studyTime: Int = 0, | ||||||
|     val endTime: Timestamp = Timestamp(0, 0) |     val endTime: Timestamp = Timestamp(0, 0), | ||||||
|  |     val taskId: String = "", | ||||||
|  |     val subjectId: String = "" | ||||||
| ) | ) | ||||||
|  | @ -2,6 +2,7 @@ package be.ugent.sel.studeez.data.local.models.timer_functional | ||||||
| 
 | 
 | ||||||
| import be.ugent.sel.studeez.data.local.models.SessionReport | import be.ugent.sel.studeez.data.local.models.SessionReport | ||||||
| import com.google.firebase.Timestamp | import com.google.firebase.Timestamp | ||||||
|  | import com.google.firebase.firestore.DocumentReference | ||||||
| 
 | 
 | ||||||
| abstract class FunctionalTimer(initialValue: Int) { | abstract class FunctionalTimer(initialValue: Int) { | ||||||
|     var time: Time = Time(initialValue) |     var time: Time = Time(initialValue) | ||||||
|  | @ -17,10 +18,12 @@ abstract class FunctionalTimer(initialValue: Int) { | ||||||
| 
 | 
 | ||||||
|     abstract fun hasCurrentCountdownEnded(): Boolean |     abstract fun hasCurrentCountdownEnded(): Boolean | ||||||
| 
 | 
 | ||||||
|     fun getSessionReport(): SessionReport { |     fun getSessionReport(subjectId: String, taskId: String): SessionReport { | ||||||
|         return SessionReport( |         return SessionReport( | ||||||
|             studyTime = totalStudyTime, |             studyTime = totalStudyTime, | ||||||
|             endTime = Timestamp.now() |             endTime = Timestamp.now(), | ||||||
|  |             taskId = taskId, | ||||||
|  |             subjectId = subjectId | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -33,4 +33,7 @@ abstract class DatabaseModule { | ||||||
| 
 | 
 | ||||||
|     @Binds |     @Binds | ||||||
|     abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO |     abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO | ||||||
|  | 
 | ||||||
|  |     @Binds | ||||||
|  |     abstract fun provideFeedDAO(impl: FirebaseFeedDAO): FeedDAO | ||||||
| } | } | ||||||
							
								
								
									
										10
									
								
								app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | package be.ugent.sel.studeez.domain | ||||||
|  | 
 | ||||||
|  | import be.ugent.sel.studeez.data.local.models.FeedEntry | ||||||
|  | import kotlinx.coroutines.flow.Flow | ||||||
|  | 
 | ||||||
|  | interface FeedDAO { | ||||||
|  | 
 | ||||||
|  |     fun getFeedEntries(): Flow<Map<String, List<FeedEntry>>> | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -12,4 +12,5 @@ interface SubjectDAO { | ||||||
|     fun deleteSubject(oldSubject: Subject) |     fun deleteSubject(oldSubject: Subject) | ||||||
| 
 | 
 | ||||||
|     fun updateSubject(newSubject: Subject) |     fun updateSubject(newSubject: Subject) | ||||||
|  |     suspend fun getSubject(subjectId: String): Subject? | ||||||
| } | } | ||||||
|  | @ -15,4 +15,6 @@ interface TaskDAO { | ||||||
|     fun deleteTask(oldTask: Task) |     fun deleteTask(oldTask: Task) | ||||||
| 
 | 
 | ||||||
|     fun toggleTaskCompleted(task: Task, completed: Boolean) |     fun toggleTaskCompleted(task: Task, completed: Boolean) | ||||||
|  | 
 | ||||||
|  |     suspend fun getTask(subjectId: String, taskId: String): Task | ||||||
| } | } | ||||||
|  | @ -1,13 +1,16 @@ | ||||||
| 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.domain.AccountDAO | import be.ugent.sel.studeez.domain.AccountDAO | ||||||
| import be.ugent.sel.studeez.domain.SubjectDAO | import be.ugent.sel.studeez.domain.SubjectDAO | ||||||
| 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.ktx.snapshots | 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.flow.map | ||||||
|  | import kotlinx.coroutines.tasks.await | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| class FireBaseSubjectDAO @Inject constructor( | class FireBaseSubjectDAO @Inject constructor( | ||||||
|  | @ -20,6 +23,10 @@ class FireBaseSubjectDAO @Inject constructor( | ||||||
|             .map { it.toObjects(Subject::class.java) } |             .map { it.toObjects(Subject::class.java) } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     override suspend fun getSubject(subjectId: String): Subject? { | ||||||
|  |         return currentUserSubjectsCollection().document(subjectId).get().await().toObject() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     override fun saveSubject(newSubject: Subject) { |     override fun saveSubject(newSubject: Subject) { | ||||||
|         currentUserSubjectsCollection().add(newSubject) |         currentUserSubjectsCollection().add(newSubject) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -8,8 +8,11 @@ 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.ktx.snapshots | 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.flow | ||||||
| import kotlinx.coroutines.flow.map | import kotlinx.coroutines.flow.map | ||||||
|  | import kotlinx.coroutines.tasks.await | ||||||
| import javax.inject.Inject | import javax.inject.Inject | ||||||
| 
 | 
 | ||||||
| class FireBaseTaskDAO @Inject constructor( | class FireBaseTaskDAO @Inject constructor( | ||||||
|  | @ -22,6 +25,10 @@ class FireBaseTaskDAO @Inject constructor( | ||||||
|             .map { it.toObjects(Task::class.java) } |             .map { it.toObjects(Task::class.java) } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     override suspend fun getTask(subjectId: String, taskId: String): Task { | ||||||
|  |         return selectedSubjectTasksCollection(subjectId).document(taskId).get().await().toObject()!! | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     override fun saveTask(newTask: Task) { |     override fun saveTask(newTask: Task) { | ||||||
|         selectedSubjectTasksCollection(newTask.subjectId).add(newTask) |         selectedSubjectTasksCollection(newTask.subjectId).add(newTask) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | package be.ugent.sel.studeez.domain.implementation | ||||||
|  | 
 | ||||||
|  | import android.icu.text.DateFormat | ||||||
|  | import be.ugent.sel.studeez.data.local.models.FeedEntry | ||||||
|  | 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.domain.FeedDAO | ||||||
|  | import be.ugent.sel.studeez.domain.SessionDAO | ||||||
|  | import be.ugent.sel.studeez.domain.TaskDAO | ||||||
|  | import com.google.firebase.Timestamp | ||||||
|  | import kotlinx.coroutines.flow.* | ||||||
|  | import javax.inject.Inject | ||||||
|  | 
 | ||||||
|  | class FirebaseFeedDAO @Inject constructor( | ||||||
|  |     private val sessionDAO: SessionDAO, | ||||||
|  |     private val taskDAO: TaskDAO, | ||||||
|  |     private val subjectDAO: FireBaseSubjectDAO | ||||||
|  | ) : 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 -> | ||||||
|  |             sessionReports | ||||||
|  |                 .map { sessionReport ->  sessionToFeedEntry(sessionReport) } | ||||||
|  |                 .sortedByDescending { it.endTime } | ||||||
|  |                 .groupBy { getFormattedTime(it) } | ||||||
|  |                 .mapValues { (_, entries) -> | ||||||
|  |                     entries | ||||||
|  |                     .groupBy { it.taskId } | ||||||
|  |                     .map { fuseFeedEntries(it.component2()) } | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun getFormattedTime(entry: FeedEntry): String { | ||||||
|  |         return DateFormat.getDateInstance().format(entry.endTime.toDate()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Givin a list of entries referencing the same task, in the same day, fuse them into one | ||||||
|  |      * feed-entry by adding the studytime and keeping the most recent end-timestamp | ||||||
|  |      */ | ||||||
|  |     private fun fuseFeedEntries(entries: List<FeedEntry>): FeedEntry = | ||||||
|  |         entries.drop(1).fold(entries[0]) { accEntry, newEntry -> | ||||||
|  |             accEntry.copy( | ||||||
|  |                 totalStudyTime = accEntry.totalStudyTime + newEntry.totalStudyTime, | ||||||
|  |                 endTime = getMostRecent(accEntry.endTime, newEntry.endTime) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     private fun getMostRecent(t1: Timestamp, t2: Timestamp): Timestamp { | ||||||
|  |         return if (t1 < t2) t2 else t1 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Convert a sessionReport to a feedEntry. Fetch Task and Subject to get names | ||||||
|  |      */ | ||||||
|  |     private suspend fun sessionToFeedEntry(sessionReport: SessionReport): FeedEntry { | ||||||
|  |         val subjectId: String = sessionReport.subjectId | ||||||
|  |         val taskId: String = sessionReport.taskId | ||||||
|  | 
 | ||||||
|  |         val task: Task = taskDAO.getTask(subjectId, taskId) | ||||||
|  |         val subject: Subject = subjectDAO.getSubject(subjectId)!! | ||||||
|  | 
 | ||||||
|  |         return FeedEntry( | ||||||
|  |             argb_color = subject.argb_color, | ||||||
|  |             subJectName = subject.name, | ||||||
|  |             taskName = task.name, | ||||||
|  |             taskId = task.id, | ||||||
|  |             subjectId = subject.id, | ||||||
|  |             totalStudyTime = sessionReport.studyTime, | ||||||
|  |             endTime = sessionReport.endTime | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -65,9 +65,9 @@ fun StudeezNavGraph( | ||||||
|         composable(StudeezDestinations.HOME_SCREEN) { |         composable(StudeezDestinations.HOME_SCREEN) { | ||||||
|             HomeRoute( |             HomeRoute( | ||||||
|                 open, |                 open, | ||||||
|                 viewModel = hiltViewModel(), |  | ||||||
|                 drawerActions = drawerActions, |                 drawerActions = drawerActions, | ||||||
|                 navigationBarActions = navigationBarActions |                 navigationBarActions = navigationBarActions, | ||||||
|  |                 feedViewModel = hiltViewModel(), | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,35 +5,45 @@ import androidx.compose.material.IconButton | ||||||
| import androidx.compose.material.icons.Icons | import androidx.compose.material.icons.Icons | ||||||
| import androidx.compose.material.icons.filled.Person | import androidx.compose.material.icons.filled.Person | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.runtime.collectAsState | ||||||
|  | import androidx.compose.runtime.getValue | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
| import be.ugent.sel.studeez.R | import be.ugent.sel.studeez.R | ||||||
| import be.ugent.sel.studeez.common.composable.BasicButton |  | ||||||
| import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate | import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate | ||||||
| import be.ugent.sel.studeez.common.composable.drawer.DrawerActions | import be.ugent.sel.studeez.common.composable.drawer.DrawerActions | ||||||
|  | import be.ugent.sel.studeez.common.composable.feed.Feed | ||||||
|  | import be.ugent.sel.studeez.common.composable.feed.FeedUiState | ||||||
|  | import be.ugent.sel.studeez.common.composable.feed.FeedViewModel | ||||||
| import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions | import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions | ||||||
| import be.ugent.sel.studeez.common.ext.basicButton | import be.ugent.sel.studeez.data.local.models.FeedEntry | ||||||
| import be.ugent.sel.studeez.resources | import be.ugent.sel.studeez.resources | ||||||
| 
 | 
 | ||||||
| @Composable | @Composable | ||||||
| fun HomeRoute( | fun HomeRoute( | ||||||
|     open: (String) -> Unit, |     open: (String) -> Unit, | ||||||
|     viewModel: HomeViewModel, |  | ||||||
|     drawerActions: DrawerActions, |     drawerActions: DrawerActions, | ||||||
|     navigationBarActions: NavigationBarActions, |     navigationBarActions: NavigationBarActions, | ||||||
|  |     feedViewModel: FeedViewModel, | ||||||
| ) { | ) { | ||||||
|  |     val feedUiState by feedViewModel.uiState.collectAsState() | ||||||
|     HomeScreen( |     HomeScreen( | ||||||
|         onStartSessionClick = { viewModel.onStartSessionClick(open) }, |  | ||||||
|         drawerActions = drawerActions, |         drawerActions = drawerActions, | ||||||
|  |         open = open, | ||||||
|         navigationBarActions = navigationBarActions, |         navigationBarActions = navigationBarActions, | ||||||
|  |         feedUiState = feedUiState, | ||||||
|  |         continueTask = { subjectId, taskId -> feedViewModel.continueTask(open, subjectId, taskId) }, | ||||||
|  |         onEmptyFeedHelp = { feedViewModel.onEmptyFeedHelp(open) } | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @Composable | @Composable | ||||||
| fun HomeScreen( | fun HomeScreen( | ||||||
|     onStartSessionClick: () -> Unit, |     open: (String) -> Unit, | ||||||
|     drawerActions: DrawerActions, |     drawerActions: DrawerActions, | ||||||
|     navigationBarActions: NavigationBarActions |     navigationBarActions: NavigationBarActions, | ||||||
|  |     feedUiState: FeedUiState, | ||||||
|  |     continueTask: (String, String) -> Unit, | ||||||
|  |     onEmptyFeedHelp: () -> Unit, | ||||||
| ) { | ) { | ||||||
|     PrimaryScreenTemplate( |     PrimaryScreenTemplate( | ||||||
|         title = resources().getString(R.string.home), |         title = resources().getString(R.string.home), | ||||||
|  | @ -41,9 +51,7 @@ fun HomeScreen( | ||||||
|         navigationBarActions = navigationBarActions, |         navigationBarActions = navigationBarActions, | ||||||
|         // TODO barAction = { FriendsAction() } |         // TODO barAction = { FriendsAction() } | ||||||
|     ) { |     ) { | ||||||
|         BasicButton(R.string.start_session, Modifier.basicButton()) { |         Feed(feedUiState, continueTask, onEmptyFeedHelp) | ||||||
|             onStartSessionClick() |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -61,8 +69,40 @@ fun FriendsAction() { | ||||||
| @Composable | @Composable | ||||||
| fun HomeScreenPreview() { | fun HomeScreenPreview() { | ||||||
|     HomeScreen( |     HomeScreen( | ||||||
|         onStartSessionClick = {}, |  | ||||||
|         drawerActions = DrawerActions({}, {}, {}, {}, {}), |         drawerActions = DrawerActions({}, {}, {}, {}, {}), | ||||||
|         navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) |         navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), | ||||||
|  |         open = {}, | ||||||
|  |         feedUiState = FeedUiState.Succes( | ||||||
|  |             mapOf( | ||||||
|  |                 "08 May 2023" to listOf( | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFABD200, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                         totalStudyTime = 600, | ||||||
|  |                     ), | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFFFD200, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                         totalStudyTime = 20, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 "09 May 2023" to listOf( | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFFD1200, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                     ), | ||||||
|  |                     FeedEntry( | ||||||
|  |                         argb_color = 0xFFFF5C89, | ||||||
|  |                         subJectName = "Test Subject", | ||||||
|  |                         taskName = "Test Task", | ||||||
|  |                     ), | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         ), | ||||||
|  |         continueTask = { _, _ -> run {} }, | ||||||
|  |         onEmptyFeedHelp = {} | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,19 +0,0 @@ | ||||||
| package be.ugent.sel.studeez.screens.home |  | ||||||
| 
 |  | ||||||
| import be.ugent.sel.studeez.domain.AccountDAO |  | ||||||
| import be.ugent.sel.studeez.domain.LogService |  | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations |  | ||||||
| import be.ugent.sel.studeez.screens.StudeezViewModel |  | ||||||
| import dagger.hilt.android.lifecycle.HiltViewModel |  | ||||||
| import javax.inject.Inject |  | ||||||
| 
 |  | ||||||
| @HiltViewModel |  | ||||||
| class HomeViewModel @Inject constructor( |  | ||||||
|     private val accountDAO: AccountDAO, |  | ||||||
|     logService: LogService |  | ||||||
| ) : StudeezViewModel(logService) { |  | ||||||
| 
 |  | ||||||
|     fun onStartSessionClick(open: (String) -> Unit) { |  | ||||||
|         open(StudeezDestinations.TIMER_SELECTION_SCREEN) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| package be.ugent.sel.studeez.screens.session | package be.ugent.sel.studeez.screens.session | ||||||
| 
 | 
 | ||||||
|  | import be.ugent.sel.studeez.data.SelectedTask | ||||||
| import be.ugent.sel.studeez.data.SelectedTimerState | import be.ugent.sel.studeez.data.SelectedTimerState | ||||||
| import be.ugent.sel.studeez.data.SessionReportState | import be.ugent.sel.studeez.data.SessionReportState | ||||||
|  | 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_functional.FunctionalTimer | ||||||
| import be.ugent.sel.studeez.domain.LogService | import be.ugent.sel.studeez.domain.LogService | ||||||
| import be.ugent.sel.studeez.navigation.StudeezDestinations | import be.ugent.sel.studeez.navigation.StudeezDestinations | ||||||
|  | @ -13,21 +15,22 @@ import javax.inject.Inject | ||||||
| class SessionViewModel @Inject constructor( | class SessionViewModel @Inject constructor( | ||||||
|     private val selectedTimerState: SelectedTimerState, |     private val selectedTimerState: SelectedTimerState, | ||||||
|     private val sessionReportState: SessionReportState, |     private val sessionReportState: SessionReportState, | ||||||
|  |     private val selectedTask: SelectedTask, | ||||||
|     logService: LogService |     logService: LogService | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
| 
 | 
 | ||||||
|     private val task : String = "No task selected" // placeholder for tasks implementation |     private val task : Task = selectedTask() | ||||||
| 
 | 
 | ||||||
|     fun getTimer() : FunctionalTimer { |     fun getTimer() : FunctionalTimer { | ||||||
|         return selectedTimerState.selectedTimer!! |         return selectedTimerState.selectedTimer!! | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun getTask(): String { |     fun getTask(): String { | ||||||
|         return task |         return task.name | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun endSession(openAndPopUp: (String, String) -> Unit) { |     fun endSession(openAndPopUp: (String, String) -> Unit) { | ||||||
|         sessionReportState.sessionReport = getTimer().getSessionReport() |         sessionReportState.sessionReport = getTimer().getSessionReport(task.subjectId, task.id) | ||||||
|         openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN) |         openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -30,6 +30,7 @@ data class TaskActions( | ||||||
|     val deleteTask: (Task) -> Unit, |     val deleteTask: (Task) -> Unit, | ||||||
|     val onCheckTask: (Task, Boolean) -> Unit, |     val onCheckTask: (Task, Boolean) -> Unit, | ||||||
|     val editSubject: () -> Unit, |     val editSubject: () -> Unit, | ||||||
|  |     val startTask: (Task) -> Unit | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions { | fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions { | ||||||
|  | @ -39,7 +40,8 @@ fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskAction | ||||||
|         getSubject = viewModel::getSelectedSubject, |         getSubject = viewModel::getSelectedSubject, | ||||||
|         deleteTask = viewModel::deleteTask, |         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) } | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -75,6 +77,7 @@ fun TaskScreen( | ||||||
|                         task = it, |                         task = it, | ||||||
|                         onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, |                         onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, | ||||||
|                         onDeleteTask = { taskActions.deleteTask(it) }, |                         onDeleteTask = { taskActions.deleteTask(it) }, | ||||||
|  |                         onStartTask = { taskActions.startTask(it) } | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -108,6 +111,7 @@ fun TaskScreenPreview() { | ||||||
|             {}, |             {}, | ||||||
|             { _, _ -> run {} }, |             { _, _ -> run {} }, | ||||||
|             {}, |             {}, | ||||||
|  |             {} | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package be.ugent.sel.studeez.screens.tasks | package be.ugent.sel.studeez.screens.tasks | ||||||
| 
 | 
 | ||||||
| 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.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 | ||||||
|  | @ -17,6 +18,7 @@ class TaskViewModel @Inject constructor( | ||||||
|     private val taskDAO: TaskDAO, |     private val taskDAO: TaskDAO, | ||||||
|     private val subjectDAO: SubjectDAO, |     private val subjectDAO: SubjectDAO, | ||||||
|     private val selectedSubject: SelectedSubject, |     private val selectedSubject: SelectedSubject, | ||||||
|  |     private val selectedTask: SelectedTask, | ||||||
|     logService: LogService, |     logService: LogService, | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
|     fun addTask(open: (String) -> Unit) { |     fun addTask(open: (String) -> Unit) { | ||||||
|  | @ -47,4 +49,9 @@ class TaskViewModel @Inject constructor( | ||||||
|     fun editSubject(open: (String) -> Unit) { |     fun editSubject(open: (String) -> Unit) { | ||||||
|         open(StudeezDestinations.EDIT_SUBJECT_FORM) |         open(StudeezDestinations.EDIT_SUBJECT_FORM) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fun startTask(task: Task, open: (String) -> Unit) { | ||||||
|  |         selectedTask.set(task) | ||||||
|  |         open(StudeezDestinations.TIMER_SELECTION_SCREEN) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | @ -82,12 +82,13 @@ fun TimerOverviewScreen( | ||||||
|                 items(timers.value) { timerInfo -> |                 items(timers.value) { timerInfo -> | ||||||
|                     TimerEntry( |                     TimerEntry( | ||||||
|                         timerInfo = timerInfo, |                         timerInfo = timerInfo, | ||||||
|                     ) { |                         rightButton = { | ||||||
|                             StealthButton( |                             StealthButton( | ||||||
|                                 text = R.string.edit, |                                 text = R.string.edit, | ||||||
|                                 onClick = { timerOverviewActions.onEditClick(timerInfo) } |                                 onClick = { timerOverviewActions.onEditClick(timerInfo) } | ||||||
|                             ) |                             ) | ||||||
|                         } |                         } | ||||||
|  |                     ) | ||||||
| 
 | 
 | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,10 +1,13 @@ | ||||||
| package be.ugent.sel.studeez.screens.timer_selection | package be.ugent.sel.studeez.screens.timer_selection | ||||||
| 
 | 
 | ||||||
|  | import androidx.compose.foundation.layout.padding | ||||||
| import androidx.compose.foundation.lazy.LazyColumn | import androidx.compose.foundation.lazy.LazyColumn | ||||||
| import androidx.compose.foundation.lazy.items | import androidx.compose.foundation.lazy.items | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
| import androidx.compose.runtime.collectAsState | import androidx.compose.runtime.collectAsState | ||||||
|  | import androidx.compose.ui.Modifier | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
|  | import androidx.compose.ui.unit.dp | ||||||
| import be.ugent.sel.studeez.R | import be.ugent.sel.studeez.R | ||||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||||
| import be.ugent.sel.studeez.common.composable.StealthButton | import be.ugent.sel.studeez.common.composable.StealthButton | ||||||
|  | @ -99,7 +102,10 @@ fun CustomTimerEntry( | ||||||
|             ) |             ) | ||||||
|         }, |         }, | ||||||
|         rightButton = { |         rightButton = { | ||||||
|             TimePickerButton(initialSeconds = hms.getTotalSeconds()) { chosenTime -> |             TimePickerButton( | ||||||
|  |                 initialSeconds = hms.getTotalSeconds(), | ||||||
|  |                 modifier = Modifier.padding(horizontal = 5.dp) | ||||||
|  |             ) { chosenTime -> | ||||||
|                 timerInfo.studyTime = chosenTime |                 timerInfo.studyTime = chosenTime | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import androidx.compose.runtime.getValue | ||||||
| import androidx.compose.runtime.mutableStateOf | import androidx.compose.runtime.mutableStateOf | ||||||
| import androidx.compose.runtime.remember | import androidx.compose.runtime.remember | ||||||
| import be.ugent.sel.studeez.data.SelectedTimerState | import be.ugent.sel.studeez.data.SelectedTimerState | ||||||
|  | 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.data.local.models.timer_info.TimerInfo | ||||||
| import be.ugent.sel.studeez.domain.LogService | import be.ugent.sel.studeez.domain.LogService | ||||||
| import be.ugent.sel.studeez.domain.TimerDAO | import be.ugent.sel.studeez.domain.TimerDAO | ||||||
|  | @ -21,7 +22,9 @@ class TimerSelectionViewModel @Inject constructor( | ||||||
|     logService: LogService |     logService: LogService | ||||||
| ) : StudeezViewModel(logService) { | ) : StudeezViewModel(logService) { | ||||||
| 
 | 
 | ||||||
|     var customTimerStudyTime: MutableState<Int> = mutableStateOf(0) |     var customTimerStudyTime: MutableState<Int> = mutableStateOf( | ||||||
|  |         HoursMinutesSeconds(1, 0, 0).getTotalSeconds() | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     fun getAllTimers() : Flow<List<TimerInfo>> { |     fun getAllTimers() : Flow<List<TimerInfo>> { | ||||||
|         return timerDAO.getAllTimers() |         return timerDAO.getAllTimers() | ||||||
|  |  | ||||||
|  | @ -29,6 +29,11 @@ | ||||||
|     <string name="home">Home</string> |     <string name="home">Home</string> | ||||||
|     <string name="start_session">Start session</string> |     <string name="start_session">Start session</string> | ||||||
| 
 | 
 | ||||||
|  |     <!-- Feed--> | ||||||
|  |     <string name="continue_task">Continue</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> | ||||||
|  | 
 | ||||||
|     <!-- Tasks --> |     <!-- Tasks --> | ||||||
|     <string name="tasks">Tasks</string> |     <string name="tasks">Tasks</string> | ||||||
|     <string name="task">Task</string> |     <string name="task">Task</string> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 brreynie
						brreynie