Merge branch 'development' into better_screens
This commit is contained in:
		
						commit
						654abb7690
					
				
					 21 changed files with 198 additions and 122 deletions
				
			
		|  | @ -123,9 +123,6 @@ dependencies { | |||
|     implementation 'com.google.firebase:firebase-firestore-ktx' | ||||
|     implementation 'com.google.firebase:firebase-perf-ktx' | ||||
|     implementation 'com.google.firebase:firebase-config-ktx' | ||||
| 
 | ||||
|     // Colorpicker | ||||
|     implementation 'com.github.skydoves:colorpicker-compose:1.0.2' | ||||
| } | ||||
| 
 | ||||
| // Allow references to generate code | ||||
|  |  | |||
|  | @ -0,0 +1,22 @@ | |||
| package be.ugent.sel.studeez.common.composable | ||||
| 
 | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.verticalScroll | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| 
 | ||||
| @Composable | ||||
| fun FormComposable( | ||||
|     title: String, | ||||
|     popUp: () -> Unit, | ||||
|     content: @Composable () -> Unit, | ||||
| ) { | ||||
|     SecondaryScreenTemplate(title = title, popUp = popUp) { | ||||
|         Box( | ||||
|             modifier = Modifier.verticalScroll(rememberScrollState()), | ||||
|         ) { | ||||
|             content() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -31,9 +31,13 @@ import be.ugent.sel.studeez.R.string as AppText | |||
| fun SubjectEntry( | ||||
|     subject: Subject, | ||||
|     onViewSubject: () -> Unit, | ||||
|     getTaskCount: () -> Flow<Int>, | ||||
|     getCompletedTaskCount: () -> Flow<Int>, | ||||
|     getStudyTime: () -> Flow<Int>, | ||||
| ) { | ||||
|     val studytime by getStudyTime().collectAsState(initial = 0) | ||||
|     val taskCount by getTaskCount().collectAsState(initial = 0) | ||||
|     val completedTaskCount by getCompletedTaskCount().collectAsState(initial = 0) | ||||
|     Card( | ||||
|         modifier = Modifier | ||||
|             .fillMaxWidth() | ||||
|  | @ -80,7 +84,7 @@ fun SubjectEntry( | |||
|                                 imageVector = Icons.Default.List, | ||||
|                                 contentDescription = stringResource(id = AppText.tasks) | ||||
|                             ) | ||||
|                             Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") | ||||
|                             Text(text = "${completedTaskCount}/${taskCount}") | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | @ -104,11 +108,11 @@ fun SubjectEntryPreview() { | |||
|         subject = Subject( | ||||
|             name = "Test Subject", | ||||
|             argb_color = 0xFFFFD200, | ||||
|             taskCount = 5, | ||||
|             taskCompletedCount = 2, | ||||
|         ), | ||||
|         onViewSubject = {}, | ||||
|         getStudyTime = { flowOf() } | ||||
|         getTaskCount = { flowOf() }, | ||||
|         getCompletedTaskCount = { flowOf() }, | ||||
|         getStudyTime = { flowOf() }, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  | @ -121,6 +125,8 @@ fun OverflowSubjectEntryPreview() { | |||
|             argb_color = 0xFFFFD200, | ||||
|         ), | ||||
|         onViewSubject = {}, | ||||
|         getStudyTime = { flowOf() } | ||||
|         getTaskCount = { flowOf() }, | ||||
|         getCompletedTaskCount = { flowOf() }, | ||||
|         getStudyTime = { flowOf() }, | ||||
|     ) | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| package be.ugent.sel.studeez.common.ext | ||||
| 
 | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import kotlin.random.Random | ||||
| 
 | ||||
| fun Color.Companion.generateRandomArgb(): Long { | ||||
|     val random = Random | ||||
|     val mask: Long = (0x000000FFL shl random.nextInt(0, 3)).inv() | ||||
|     return random.nextLong(0xFF000000L, 0xFFFFFFFFL) and mask | ||||
| } | ||||
|  | @ -8,10 +8,6 @@ data class Subject( | |||
|     val name: String = "", | ||||
|     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 { | ||||
|  |  | |||
|  | @ -6,14 +6,13 @@ class FunctionalPomodoroTimer( | |||
|     val repeats: Int | ||||
| ) : FunctionalTimer(studyTime) { | ||||
| 
 | ||||
|     var breaksRemaining = repeats | ||||
|     var breaksRemaining = repeats - 1 | ||||
|     var isInBreak = false | ||||
| 
 | ||||
|     override fun tick() { | ||||
|         if (hasEnded()) { | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         if (hasCurrentCountdownEnded()) { | ||||
|             if (isInBreak) { | ||||
|                 breaksRemaining-- | ||||
|  |  | |||
|  | @ -13,8 +13,10 @@ interface SubjectDAO { | |||
| 
 | ||||
|     fun updateSubject(newSubject: Subject) | ||||
| 
 | ||||
|     suspend fun getTaskCount(subject: Subject): Int | ||||
|     suspend fun getCompletedTaskCount(subject: Subject): Int | ||||
|     suspend fun archiveSubject(subject: Subject) | ||||
| 
 | ||||
|     fun getTaskCount(subject: Subject): Flow<Int> | ||||
|     fun getCompletedTaskCount(subject: Subject): Flow<Int> | ||||
|     fun getStudyTime(subject: Subject): Flow<Int> | ||||
| 
 | ||||
|     suspend fun getSubject(subjectId: String): Subject? | ||||
|  |  | |||
|  | @ -1,11 +1,13 @@ | |||
| package be.ugent.sel.studeez.domain.implementation | ||||
| 
 | ||||
| import android.util.Log | ||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | ||||
| import be.ugent.sel.studeez.data.local.models.task.SubjectDocument | ||||
| import be.ugent.sel.studeez.data.local.models.task.Task | ||||
| import be.ugent.sel.studeez.data.local.models.task.TaskDocument | ||||
| 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 | ||||
|  | @ -15,6 +17,7 @@ import kotlinx.coroutines.flow.Flow | |||
| import kotlinx.coroutines.flow.map | ||||
| import kotlinx.coroutines.tasks.await | ||||
| import javax.inject.Inject | ||||
| import kotlin.collections.count | ||||
| 
 | ||||
| class FireBaseSubjectDAO @Inject constructor( | ||||
|     private val firestore: FirebaseFirestore, | ||||
|  | @ -26,13 +29,6 @@ class FireBaseSubjectDAO @Inject constructor( | |||
|             .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? { | ||||
|  | @ -51,23 +47,26 @@ class FireBaseSubjectDAO @Inject constructor( | |||
|         currentUserSubjectsCollection().document(newSubject.id).set(newSubject) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun getTaskCount(subject: Subject): Int { | ||||
|         return subjectTasksCollection(subject) | ||||
|     override suspend fun archiveSubject(subject: Subject) { | ||||
|         currentUserSubjectsCollection().document(subject.id).update(SubjectDocument.archived, true) | ||||
|         currentUserSubjectsCollection().document(subject.id) | ||||
|             .collection(FireBaseCollections.TASK_COLLECTION) | ||||
|             .taskNotArchived() | ||||
|             .count() | ||||
|             .get(AggregateSource.SERVER) | ||||
|             .await() | ||||
|             .count.toInt() | ||||
|             .get().await() | ||||
|             .documents | ||||
|             .forEach { | ||||
|                 it.reference.update(TaskDocument.archived, true) | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun getCompletedTaskCount(subject: Subject): Int { | ||||
|         return subjectTasksCollection(subject) | ||||
|             .taskNotArchived() | ||||
|             .taskNotCompleted() | ||||
|             .count() | ||||
|             .get(AggregateSource.SERVER) | ||||
|             .await() | ||||
|             .count.toInt() | ||||
|     override fun getTaskCount(subject: Subject): Flow<Int> { | ||||
|         return taskDAO.getTasks(subject) | ||||
|             .map(List<Task>::count) | ||||
|     } | ||||
| 
 | ||||
|     override fun getCompletedTaskCount(subject: Subject): Flow<Int> { | ||||
|         return taskDAO.getTasks(subject) | ||||
|             .map { tasks -> tasks.count { it.completed && !it.archived } } | ||||
|     } | ||||
| 
 | ||||
|     override fun getStudyTime(subject: Subject): Flow<Int> { | ||||
|  |  | |||
|  | @ -25,9 +25,9 @@ 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.subjects.SubjectRoute | ||||
| import be.ugent.sel.studeez.screens.tasks.TaskRoute | ||||
| 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.TaskRoute | ||||
| 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 | ||||
|  | @ -51,6 +51,7 @@ fun StudeezNavGraph( | |||
|     val open: (String) -> Unit = { appState.navigate(it) } | ||||
|     val openAndPopUp: (String, String) -> Unit = | ||||
|         { route, popUp -> appState.navigateAndPopUp(route, popUp) } | ||||
|     val clearAndNavigate: (route: String) -> Unit = { route -> appState.clearAndNavigate(route) } | ||||
| 
 | ||||
|     val drawerActions: DrawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp) | ||||
|     val navigationBarActions: NavigationBarActions = | ||||
|  | @ -200,7 +201,7 @@ fun StudeezNavGraph( | |||
| 
 | ||||
|         composable(StudeezDestinations.SESSION_RECAP) { | ||||
|             SessionRecapRoute( | ||||
|                 openAndPopUp = openAndPopUp, | ||||
|                 clearAndNavigate = clearAndNavigate, | ||||
|                 viewModel = hiltViewModel() | ||||
|             ) | ||||
|         } | ||||
|  |  | |||
|  | @ -4,18 +4,13 @@ import android.media.MediaPlayer | |||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.foundation.shape.CircleShape | ||||
| 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.TextAlign | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import be.ugent.sel.studeez.R | ||||
| import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer | ||||
| import be.ugent.sel.studeez.resources | ||||
| import be.ugent.sel.studeez.R.string as AppText | ||||
|  | @ -37,23 +32,31 @@ class BreakSessionScreen( | |||
|             verticalAlignment = Alignment.CenterVertically, | ||||
|             horizontalArrangement = Arrangement.Center, | ||||
|         ) { | ||||
|             repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) { | ||||
|             if (funPomoDoroTimer.hasEnded()) { | ||||
|                 repeat(funPomoDoroTimer.repeats) { | ||||
|                     Dot(Color.Green) | ||||
|                 } | ||||
|             } else { | ||||
|                 repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining - 1) { | ||||
|                     Dot(color = Color.DarkGray) | ||||
|                 } | ||||
|                 if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) | ||||
|             repeat(funPomoDoroTimer.breaksRemaining - 1) { | ||||
|                 repeat(funPomoDoroTimer.breaksRemaining) { | ||||
|                     Dot(color = Color.Gray) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Composable | ||||
|     private fun Dot(color: Color) { | ||||
|         Box(modifier = Modifier | ||||
|         Box( | ||||
|             modifier = Modifier | ||||
|                 .padding(5.dp) | ||||
|                 .size(10.dp) | ||||
|                 .clip(CircleShape) | ||||
|             .background(color)) | ||||
|                 .background(color) | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     @Composable | ||||
|  |  | |||
|  | @ -32,24 +32,24 @@ data class SessionRecapActions( | |||
| 
 | ||||
| fun getSessionRecapActions( | ||||
|     viewModel: SessionRecapViewModel, | ||||
|     openAndPopUp: (String, String) -> Unit, | ||||
|     clearAndNavigate: (String) -> Unit, | ||||
| ): SessionRecapActions { | ||||
|     return SessionRecapActions( | ||||
|         viewModel::getSessionReport, | ||||
|         { viewModel.saveSession(openAndPopUp) }, | ||||
|         { viewModel.discardSession(openAndPopUp) } | ||||
|         { viewModel.saveSession(clearAndNavigate) }, | ||||
|         { viewModel.discardSession(clearAndNavigate) } | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun SessionRecapRoute( | ||||
|     openAndPopUp: (String, String) -> Unit, | ||||
|     clearAndNavigate: (String) -> Unit, | ||||
|     modifier: Modifier = Modifier, | ||||
|     viewModel: SessionRecapViewModel, | ||||
| ) { | ||||
|     SessionRecapScreen( | ||||
|         modifier = modifier, | ||||
|         getSessionRecapActions(viewModel, openAndPopUp) | ||||
|         getSessionRecapActions(viewModel, clearAndNavigate) | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,15 +24,15 @@ class SessionRecapViewModel @Inject constructor( | |||
|         return selectedSessionReport() | ||||
|     } | ||||
| 
 | ||||
|     fun saveSession(open: (String, String) -> Unit) { | ||||
|     fun saveSession(open: (String) -> Unit) { | ||||
|         sessionDAO.saveSession(getSessionReport()) | ||||
|         val newTask = | ||||
|             selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime) | ||||
|         taskDAO.updateTask(newTask) | ||||
|         open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) | ||||
|         open(StudeezDestinations.HOME_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun discardSession(open: (String, String) -> Unit) { | ||||
|         open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) | ||||
|     fun discardSession(open: (String) -> Unit) { | ||||
|         open(StudeezDestinations.HOME_SCREEN) | ||||
|     } | ||||
| } | ||||
|  | @ -36,6 +36,8 @@ fun SubjectRoute( | |||
|         navigationBarActions = navigationBarActions, | ||||
|         onAddSubject = { viewModel.onAddSubject(open) }, | ||||
|         onViewSubject = { viewModel.onViewSubject(it, open) }, | ||||
|         getTaskCount = viewModel::getTaskCount, | ||||
|         getCompletedTaskCount = viewModel::getCompletedTaskCount, | ||||
|         getStudyTime = viewModel::getStudyTime, | ||||
|         uiState, | ||||
|     ) | ||||
|  | @ -47,6 +49,8 @@ fun SubjectScreen( | |||
|     navigationBarActions: NavigationBarActions, | ||||
|     onAddSubject: () -> Unit, | ||||
|     onViewSubject: (Subject) -> Unit, | ||||
|     getTaskCount: (Subject) -> Flow<Int>, | ||||
|     getCompletedTaskCount: (Subject) -> Flow<Int>, | ||||
|     getStudyTime: (Subject) -> Flow<Int>, | ||||
|     uiState: SubjectUiState, | ||||
| ) { | ||||
|  | @ -76,6 +80,8 @@ fun SubjectScreen( | |||
|                             SubjectEntry( | ||||
|                                 subject = it, | ||||
|                                 onViewSubject = { onViewSubject(it) }, | ||||
|                                 getTaskCount = { getTaskCount(it) }, | ||||
|                                 getCompletedTaskCount = { getCompletedTaskCount(it) }, | ||||
|                                 getStudyTime = { getStudyTime(it) }, | ||||
|                             ) | ||||
|                         } | ||||
|  | @ -94,13 +100,14 @@ fun SubjectScreenPreview() { | |||
|         navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), | ||||
|         onAddSubject = {}, | ||||
|         onViewSubject = {}, | ||||
|         getTaskCount = { flowOf() }, | ||||
|         getCompletedTaskCount = { flowOf() }, | ||||
|         getStudyTime = { flowOf() }, | ||||
|         uiState = SubjectUiState.Succes( | ||||
|             listOf( | ||||
|                 Subject( | ||||
|                     name = "Test Subject", | ||||
|                     argb_color = 0xFFFFD200, | ||||
|                     taskCount = 5, taskCompletedCount = 2, | ||||
|                 ) | ||||
|             ) | ||||
|         ) | ||||
|  | @ -115,7 +122,9 @@ fun SubjectScreenLoadingPreview() { | |||
|         navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), | ||||
|         onAddSubject = {}, | ||||
|         onViewSubject = {}, | ||||
|         getTaskCount = { flowOf() }, | ||||
|         getCompletedTaskCount = { flowOf() }, | ||||
|         getStudyTime = { flowOf() }, | ||||
|         uiState = SubjectUiState.Loading | ||||
|         uiState = SubjectUiState.Loading, | ||||
|     ) | ||||
| } | ||||
|  | @ -30,6 +30,14 @@ class SubjectViewModel @Inject constructor( | |||
|         open(StudeezDestinations.ADD_SUBJECT_FORM) | ||||
|     } | ||||
| 
 | ||||
|     fun getTaskCount(subject: Subject): Flow<Int> { | ||||
|         return subjectDAO.getTaskCount(subject) | ||||
|     } | ||||
| 
 | ||||
|     fun getCompletedTaskCount(subject: Subject): Flow<Int> { | ||||
|         return subjectDAO.getCompletedTaskCount(subject) | ||||
|     } | ||||
| 
 | ||||
|     fun getStudyTime(subject: Subject): Flow<Int> { | ||||
|         return subjectDAO.getStudyTime(subject) | ||||
|     } | ||||
|  |  | |||
|  | @ -2,20 +2,28 @@ package be.ugent.sel.studeez.screens.subjects.form | |||
| 
 | ||||
| import androidx.annotation.StringRes | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.material.OutlinedTextField | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.ButtonDefaults | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.rememberCoroutineScope | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.Color | ||||
| 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.BasicButton | ||||
| import be.ugent.sel.studeez.common.composable.DeleteButton | ||||
| import be.ugent.sel.studeez.common.composable.FormComposable | ||||
| import be.ugent.sel.studeez.common.composable.LabelledInputField | ||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||
| import be.ugent.sel.studeez.common.ext.basicButton | ||||
| import be.ugent.sel.studeez.common.ext.fieldModifier | ||||
| import be.ugent.sel.studeez.common.ext.generateRandomArgb | ||||
| import be.ugent.sel.studeez.resources | ||||
| import kotlinx.coroutines.launch | ||||
| import be.ugent.sel.studeez.R.string as AppText | ||||
| 
 | ||||
| @Composable | ||||
|  | @ -31,7 +39,7 @@ fun SubjectCreateRoute( | |||
|         uiState = uiState, | ||||
|         onConfirm = { viewModel.onCreate(openAndPopUp) }, | ||||
|         onNameChange = viewModel::onNameChange, | ||||
|         onColorChange = {}, | ||||
|         onColorChange = viewModel::onColorChange, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  | @ -42,19 +50,22 @@ fun SubjectEditRoute( | |||
|     viewModel: SubjectEditFormViewModel, | ||||
| ) { | ||||
|     val uiState by viewModel.uiState | ||||
|     val coroutineScope = rememberCoroutineScope() | ||||
|     SubjectForm( | ||||
|         title = AppText.edit_subject, | ||||
|         goBack = goBack, | ||||
|         uiState = uiState, | ||||
|         onConfirm = { viewModel.onEdit(openAndPopUp) }, | ||||
|         onNameChange = viewModel::onNameChange, | ||||
|         onColorChange = {}, | ||||
|         onColorChange = viewModel::onColorChange, | ||||
|     ) { | ||||
|         DeleteButton(text = AppText.delete_subject) { | ||||
|             coroutineScope.launch { | ||||
|                 viewModel.onDelete(openAndPopUp) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun SubjectForm( | ||||
|  | @ -63,21 +74,21 @@ fun SubjectForm( | |||
|     uiState: SubjectFormUiState, | ||||
|     onConfirm: () -> Unit, | ||||
|     onNameChange: (String) -> Unit, | ||||
|     onColorChange: (Color) -> Unit, | ||||
|     onColorChange: (Long) -> Unit, | ||||
|     extraButton: @Composable () -> Unit = {}, | ||||
| ) { | ||||
|     SecondaryScreenTemplate( | ||||
|     FormComposable( | ||||
|         title = resources().getString(title), | ||||
|         popUp = goBack, | ||||
|     ) { | ||||
|         Column { | ||||
|             OutlinedTextField( | ||||
|             LabelledInputField( | ||||
|                 singleLine = true, | ||||
|                 value = uiState.name, | ||||
|                 onValueChange = onNameChange, | ||||
|                 placeholder = { Text(stringResource(id = AppText.name)) }, | ||||
|                 modifier = Modifier.fieldModifier(), | ||||
|                 onNewValue = onNameChange, | ||||
|                 label = AppText.name, | ||||
|             ) | ||||
|             ColorPicker(onColorChange, uiState) | ||||
|             BasicButton( | ||||
|                 text = AppText.confirm, | ||||
|                 modifier = Modifier.basicButton(), | ||||
|  | @ -88,6 +99,24 @@ fun SubjectForm( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun ColorPicker( | ||||
|     onColorChange: (Long) -> Unit, | ||||
|     uiState: SubjectFormUiState, | ||||
| ) { | ||||
|     Button( | ||||
|         onClick = { onColorChange(Color.generateRandomArgb()) }, | ||||
|         modifier = Modifier.fieldModifier(), | ||||
|         colors = ButtonDefaults.buttonColors( | ||||
|             backgroundColor = Color(uiState.color), | ||||
|             contentColor = Color.White, | ||||
|         ), | ||||
|         shape = RoundedCornerShape(4.dp), | ||||
|     ) { | ||||
|         Text(text = stringResource(id = AppText.regenerate_color)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun AddSubjectFormPreview() { | ||||
|  |  | |||
|  | @ -1,6 +1,9 @@ | |||
| package be.ugent.sel.studeez.screens.subjects.form | ||||
| 
 | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import be.ugent.sel.studeez.common.ext.generateRandomArgb | ||||
| 
 | ||||
| data class SubjectFormUiState( | ||||
|     val name: String = "", | ||||
|     val color: Long = 0xFFFFD200, | ||||
|     val color: Long = Color.generateRandomArgb(), | ||||
| ) | ||||
|  | @ -2,10 +2,13 @@ package be.ugent.sel.studeez.screens.subjects.form | |||
| 
 | ||||
| import androidx.compose.runtime.MutableState | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import be.ugent.sel.studeez.common.ext.generateRandomArgb | ||||
| import be.ugent.sel.studeez.data.SelectedSubject | ||||
| import be.ugent.sel.studeez.data.local.models.task.Subject | ||||
| 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 | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
|  | @ -59,6 +62,7 @@ class SubjectCreateFormViewModel @Inject constructor( | |||
| @HiltViewModel | ||||
| class SubjectEditFormViewModel @Inject constructor( | ||||
|     subjectDAO: SubjectDAO, | ||||
|     private val taskDAO: TaskDAO, | ||||
|     selectedSubject: SelectedSubject, | ||||
|     logService: LogService, | ||||
| ) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) { | ||||
|  | @ -69,17 +73,19 @@ class SubjectEditFormViewModel @Inject constructor( | |||
|         ) | ||||
|     ) | ||||
| 
 | ||||
|     fun onDelete(openAndPopUp: (String, String) -> Unit) { | ||||
|         subjectDAO.updateSubject(selectedSubject().copy(archived = true)) | ||||
|     suspend fun onDelete(openAndPopUp: (String, String) -> Unit) { | ||||
|         subjectDAO.archiveSubject(selectedSubject()) | ||||
|         openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) | ||||
|     } | ||||
| 
 | ||||
|     fun onEdit(openAndPopUp: (String, String) -> Unit) { | ||||
|         val newSubject = selectedSubject().copy( | ||||
|         selectedSubject.set( | ||||
|             selectedSubject().copy( | ||||
|                 name = name, | ||||
|                 argb_color = color, | ||||
|             ) | ||||
|         subjectDAO.updateSubject(newSubject) | ||||
|         ) | ||||
|         subjectDAO.updateSubject(selectedSubject()) | ||||
|         openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) | ||||
|     } | ||||
| } | ||||
|  | @ -11,7 +11,7 @@ import androidx.compose.ui.res.stringResource | |||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import be.ugent.sel.studeez.common.composable.BasicButton | ||||
| import be.ugent.sel.studeez.common.composable.DeleteButton | ||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||
| import be.ugent.sel.studeez.common.composable.FormComposable | ||||
| import be.ugent.sel.studeez.common.ext.basicButton | ||||
| import be.ugent.sel.studeez.common.ext.fieldModifier | ||||
| import be.ugent.sel.studeez.resources | ||||
|  | @ -62,7 +62,7 @@ fun TaskForm( | |||
|     onNameChange: (String) -> Unit, | ||||
|     extraButton: @Composable () -> Unit = {} | ||||
| ) { | ||||
|     SecondaryScreenTemplate( | ||||
|     FormComposable( | ||||
|         title = resources().getString(title), | ||||
|         popUp = goBack, | ||||
|     ) { | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ package be.ugent.sel.studeez.screens.timer_form | |||
| import androidx.annotation.StringRes | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||
| import be.ugent.sel.studeez.common.composable.FormComposable | ||||
| import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo | ||||
| import be.ugent.sel.studeez.R.string as AppText | ||||
| 
 | ||||
|  | @ -36,7 +36,10 @@ fun TimerFormScreen( | |||
| ) { | ||||
|     val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen()) | ||||
| 
 | ||||
|     SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) { | ||||
|     FormComposable( | ||||
|         title = stringResource(id = label), | ||||
|         popUp = popUp | ||||
|     ) { | ||||
|         timerFormScreen(onConfirmClick) | ||||
|     } | ||||
| } | ||||
|  | @ -1,17 +1,7 @@ | |||
| package be.ugent.sel.studeez.screens.timer_form.form_screens | ||||
| 
 | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.fillMaxHeight | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.verticalScroll | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.Modifier | ||||
| import be.ugent.sel.studeez.R | ||||
| import be.ugent.sel.studeez.common.composable.BasicButton | ||||
|  | @ -32,14 +22,7 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { | |||
|         timerInfo.name = name | ||||
|         timerInfo.description = description | ||||
| 
 | ||||
|         Column( | ||||
|             verticalArrangement = Arrangement.SpaceBetween, | ||||
|             modifier = Modifier.fillMaxHeight().verticalScroll(rememberScrollState()), | ||||
|         ) { | ||||
|             Column( | ||||
|                 modifier = Modifier.fillMaxWidth(), | ||||
|                 horizontalAlignment = Alignment.CenterHorizontally | ||||
|             ) { | ||||
|         Column { | ||||
| 
 | ||||
|             // Fields that every timer shares (ommited id) | ||||
|             LabelledInputField( | ||||
|  | @ -57,7 +40,6 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { | |||
| 
 | ||||
|             ExtraFields() | ||||
| 
 | ||||
|             } | ||||
|             BasicButton(R.string.save, Modifier.basicButton()) { | ||||
|                 onSaveClick(timerInfo) | ||||
|             } | ||||
|  |  | |||
|  | @ -46,6 +46,7 @@ | |||
|     <string name="delete_subject">Delete Subject</string> | ||||
|     <string name="delete_task">Delete Task</string> | ||||
|     <string name="view_tasks">View</string> | ||||
|     <string name="regenerate_color">Regenerate Color</string> | ||||
| 
 | ||||
|     <!-- Sessions --> | ||||
|     <string name="sessions_temp_description">Looks like you found the sessions screen! In here, your upcoming studying sessions with friends will be listed. You can accept invites or edit your own.</string> <!-- TODO Remove this description line once implemented. --> | ||||
|  |  | |||
		Reference in a new issue
	
	 brreynie
						brreynie