resolve merge conflicts

This commit is contained in:
lbarraga 2023-05-15 21:47:28 +02:00
commit 924e136153
30 changed files with 469 additions and 184 deletions

1
.idea/misc.xml generated
View file

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">

View file

@ -123,9 +123,6 @@ dependencies {
implementation 'com.google.firebase:firebase-firestore-ktx' implementation 'com.google.firebase:firebase-firestore-ktx'
implementation 'com.google.firebase:firebase-perf-ktx' implementation 'com.google.firebase:firebase-perf-ktx'
implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-config-ktx'
// Colorpicker
implementation 'com.github.skydoves:colorpicker-compose:1.0.2'
} }
// Allow references to generate code // Allow references to generate code

View file

@ -2,7 +2,6 @@ package be.ugent.sel.studeez.common.composable
import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.FloatingActionButton import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon import androidx.compose.material.Icon

View file

@ -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()
}
}
}

View file

@ -0,0 +1,39 @@
package be.ugent.sel.studeez.common.composable
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
@Composable
fun ImageBackgroundButton(
paint: Painter,
str: String,
background2: Color,
setBackground1: (Color) -> Unit,
setBackground2: (Color) -> Unit
) {
Image(
painter = paint,
str,
modifier = Modifier
.clickable {
if (background2 == Color.Transparent) {
setBackground1(Color.LightGray)
setBackground2(Color.Transparent)
} else {
setBackground2(Color.Transparent)
}
}
.border(
width = 2.dp,
color = background2,
shape = RoundedCornerShape(16.dp)
)
)
}

View file

@ -3,7 +3,6 @@ package be.ugent.sel.studeez.common.composable
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -22,7 +21,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import kotlin.math.sin
import be.ugent.sel.studeez.R.drawable as AppIcon import be.ugent.sel.studeez.R.drawable as AppIcon
import be.ugent.sel.studeez.R.string as AppText import be.ugent.sel.studeez.R.string as AppText
@ -47,7 +45,7 @@ fun LabelledInputField(
value: String, value: String,
onNewValue: (String) -> Unit, onNewValue: (String) -> Unit,
@StringRes label: Int, @StringRes label: Int,
singleLine: Boolean = false singleLine: Boolean = true
) { ) {
OutlinedTextField( OutlinedTextField(
value = value, value = value,
@ -119,7 +117,9 @@ fun LabeledErrorTextField(
initialValue: String, initialValue: String,
@StringRes label: Int, @StringRes label: Int,
singleLine: Boolean = false, singleLine: Boolean = false,
errorText: Int, isValid: MutableState<Boolean> = remember { mutableStateOf(true) },
isFirst: MutableState<Boolean> = remember { mutableStateOf(false) },
@StringRes errorText: Int,
keyboardType: KeyboardType, keyboardType: KeyboardType,
predicate: (String) -> Boolean, predicate: (String) -> Boolean,
onNewCorrectValue: (String) -> Unit onNewCorrectValue: (String) -> Unit
@ -128,31 +128,28 @@ fun LabeledErrorTextField(
mutableStateOf(initialValue) mutableStateOf(initialValue)
} }
var isValid by remember {
mutableStateOf(predicate(value))
}
Column { Column {
OutlinedTextField( OutlinedTextField(
modifier = modifier.fieldModifier(), modifier = modifier.fieldModifier(),
value = value, value = value,
onValueChange = { newText -> onValueChange = { newText ->
isFirst.value = false
value = newText value = newText
isValid = predicate(value) isValid.value = predicate(value)
if (isValid) { if (isValid.value) {
onNewCorrectValue(newText) onNewCorrectValue(newText)
} }
}, },
singleLine = singleLine, singleLine = singleLine,
label = { Text(text = stringResource(id = label)) }, label = { Text(text = stringResource(id = label)) },
isError = !isValid, isError = !isValid.value && !isFirst.value,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
keyboardType = keyboardType, keyboardType = keyboardType,
imeAction = ImeAction.Done imeAction = ImeAction.Done
) )
) )
if (!isValid) { if (!isValid.value && !isFirst.value) {
Text( Text(
modifier = Modifier.padding(start = 16.dp), modifier = Modifier.padding(start = 16.dp),
text = stringResource(id = errorText), text = stringResource(id = errorText),

View file

@ -31,9 +31,13 @@ import be.ugent.sel.studeez.R.string as AppText
fun SubjectEntry( fun SubjectEntry(
subject: Subject, subject: Subject,
onViewSubject: () -> Unit, onViewSubject: () -> Unit,
getTaskCount: () -> Flow<Int>,
getCompletedTaskCount: () -> Flow<Int>,
getStudyTime: () -> Flow<Int>, getStudyTime: () -> Flow<Int>,
) { ) {
val studytime by getStudyTime().collectAsState(initial = 0) val studytime by getStudyTime().collectAsState(initial = 0)
val taskCount by getTaskCount().collectAsState(initial = 0)
val completedTaskCount by getCompletedTaskCount().collectAsState(initial = 0)
Card( Card(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -80,7 +84,7 @@ fun SubjectEntry(
imageVector = Icons.Default.List, imageVector = Icons.Default.List,
contentDescription = stringResource(id = AppText.tasks) contentDescription = stringResource(id = AppText.tasks)
) )
Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") Text(text = "${completedTaskCount}/${taskCount}")
} }
} }
} }
@ -104,11 +108,11 @@ fun SubjectEntryPreview() {
subject = Subject( subject = Subject(
name = "Test Subject", name = "Test Subject",
argb_color = 0xFFFFD200, argb_color = 0xFFFFD200,
taskCount = 5,
taskCompletedCount = 2,
), ),
onViewSubject = {}, onViewSubject = {},
getStudyTime = { flowOf() } getTaskCount = { flowOf() },
getCompletedTaskCount = { flowOf() },
getStudyTime = { flowOf() },
) )
} }
@ -121,6 +125,8 @@ fun OverflowSubjectEntryPreview() {
argb_color = 0xFFFFD200, argb_color = 0xFFFFD200,
), ),
onViewSubject = {}, onViewSubject = {},
getStudyTime = { flowOf() } getTaskCount = { flowOf() },
getCompletedTaskCount = { flowOf() },
getStudyTime = { flowOf() },
) )
} }

View file

@ -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
}

View file

@ -8,10 +8,6 @@ data class Subject(
val name: String = "", val name: String = "",
val argb_color: Long = 0, val argb_color: Long = 0,
var archived: Boolean = false, var archived: Boolean = false,
@get:Exclude @set:Exclude
var taskCount: Int = 0,
@get:Exclude @set:Exclude
var taskCompletedCount: Int = 0,
) )
object SubjectDocument { object SubjectDocument {

View file

@ -13,7 +13,6 @@ class FunctionalPomodoroTimer(
if (hasEnded()) { if (hasEnded()) {
return return
} }
if (hasCurrentCountdownEnded()) { if (hasCurrentCountdownEnded()) {
if (isInBreak) { if (isInBreak) {
breaksRemaining-- breaksRemaining--

View file

@ -13,8 +13,10 @@ interface SubjectDAO {
fun updateSubject(newSubject: Subject) fun updateSubject(newSubject: Subject)
suspend fun getTaskCount(subject: Subject): Int suspend fun archiveSubject(subject: Subject)
suspend fun getCompletedTaskCount(subject: Subject): Int
fun getTaskCount(subject: Subject): Flow<Int>
fun getCompletedTaskCount(subject: Subject): Flow<Int>
fun getStudyTime(subject: Subject): Flow<Int> fun getStudyTime(subject: Subject): Flow<Int>
suspend fun getSubject(subjectId: String): Subject? suspend fun getSubject(subjectId: String): Subject?

View file

@ -1,11 +1,13 @@
package be.ugent.sel.studeez.domain.implementation 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.Subject
import be.ugent.sel.studeez.data.local.models.task.SubjectDocument 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.AccountDAO
import be.ugent.sel.studeez.domain.SubjectDAO import be.ugent.sel.studeez.domain.SubjectDAO
import be.ugent.sel.studeez.domain.TaskDAO import be.ugent.sel.studeez.domain.TaskDAO
import com.google.firebase.firestore.AggregateSource
import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.Query import com.google.firebase.firestore.Query
@ -15,6 +17,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.tasks.await import kotlinx.coroutines.tasks.await
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.count
class FireBaseSubjectDAO @Inject constructor( class FireBaseSubjectDAO @Inject constructor(
private val firestore: FirebaseFirestore, private val firestore: FirebaseFirestore,
@ -26,13 +29,6 @@ class FireBaseSubjectDAO @Inject constructor(
.subjectNotArchived() .subjectNotArchived()
.snapshots() .snapshots()
.map { it.toObjects(Subject::class.java) } .map { it.toObjects(Subject::class.java) }
.map { subjects ->
subjects.map { subject ->
subject.taskCount = getTaskCount(subject)
subject.taskCompletedCount = getCompletedTaskCount(subject)
subject
}
}
} }
override suspend fun getSubject(subjectId: String): Subject? { override suspend fun getSubject(subjectId: String): Subject? {
@ -51,23 +47,26 @@ class FireBaseSubjectDAO @Inject constructor(
currentUserSubjectsCollection().document(newSubject.id).set(newSubject) currentUserSubjectsCollection().document(newSubject.id).set(newSubject)
} }
override suspend fun getTaskCount(subject: Subject): Int { override suspend fun archiveSubject(subject: Subject) {
return subjectTasksCollection(subject) currentUserSubjectsCollection().document(subject.id).update(SubjectDocument.archived, true)
currentUserSubjectsCollection().document(subject.id)
.collection(FireBaseCollections.TASK_COLLECTION)
.taskNotArchived() .taskNotArchived()
.count() .get().await()
.get(AggregateSource.SERVER) .documents
.await() .forEach {
.count.toInt() it.reference.update(TaskDocument.archived, true)
}
} }
override suspend fun getCompletedTaskCount(subject: Subject): Int { override fun getTaskCount(subject: Subject): Flow<Int> {
return subjectTasksCollection(subject) return taskDAO.getTasks(subject)
.taskNotArchived() .map(List<Task>::count)
.taskNotCompleted() }
.count()
.get(AggregateSource.SERVER) override fun getCompletedTaskCount(subject: Subject): Flow<Int> {
.await() return taskDAO.getTasks(subject)
.count.toInt() .map { tasks -> tasks.count { it.completed && !it.archived } }
} }
override fun getStudyTime(subject: Subject): Flow<Int> { override fun getStudyTime(subject: Subject): Flow<Int> {

View file

@ -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.sign_up.SignUpRoute
import be.ugent.sel.studeez.screens.splash.SplashRoute import be.ugent.sel.studeez.screens.splash.SplashRoute
import be.ugent.sel.studeez.screens.subjects.SubjectRoute 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.SubjectCreateRoute
import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute 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.TaskCreateRoute
import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute
import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute
@ -51,6 +51,7 @@ fun StudeezNavGraph(
val open: (String) -> Unit = { appState.navigate(it) } val open: (String) -> Unit = { appState.navigate(it) }
val openAndPopUp: (String, String) -> Unit = val openAndPopUp: (String, String) -> Unit =
{ route, popUp -> appState.navigateAndPopUp(route, popUp) } { route, popUp -> appState.navigateAndPopUp(route, popUp) }
val clearAndNavigate: (route: String) -> Unit = { route -> appState.clearAndNavigate(route) }
val drawerActions: DrawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp) val drawerActions: DrawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp)
val navigationBarActions: NavigationBarActions = val navigationBarActions: NavigationBarActions =
@ -200,7 +201,7 @@ fun StudeezNavGraph(
composable(StudeezDestinations.SESSION_RECAP) { composable(StudeezDestinations.SESSION_RECAP) {
SessionRecapRoute( SessionRecapRoute(
openAndPopUp = openAndPopUp, clearAndNavigate = clearAndNavigate,
viewModel = hiltViewModel() viewModel = hiltViewModel()
) )
} }

View file

@ -1,13 +1,24 @@
package be.ugent.sel.studeez.screens.session_recap package be.ugent.sel.studeez.screens.session_recap
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.material.ButtonDefaults import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
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.R
import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.ImageBackgroundButton
import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.data.local.models.SessionReport import be.ugent.sel.studeez.data.local.models.SessionReport
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
@ -21,24 +32,24 @@ data class SessionRecapActions(
fun getSessionRecapActions( fun getSessionRecapActions(
viewModel: SessionRecapViewModel, viewModel: SessionRecapViewModel,
openAndPopUp: (String, String) -> Unit, clearAndNavigate: (String) -> Unit,
): SessionRecapActions { ): SessionRecapActions {
return SessionRecapActions( return SessionRecapActions(
viewModel::getSessionReport, viewModel::getSessionReport,
{viewModel.saveSession(openAndPopUp)}, { viewModel.saveSession(clearAndNavigate) },
{viewModel.discardSession(openAndPopUp)} { viewModel.discardSession(clearAndNavigate) }
) )
} }
@Composable @Composable
fun SessionRecapRoute( fun SessionRecapRoute(
openAndPopUp: (String, String) -> Unit, clearAndNavigate: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: SessionRecapViewModel, viewModel: SessionRecapViewModel,
) { ) {
SessionRecapScreen( SessionRecapScreen(
modifier = modifier, modifier = modifier,
getSessionRecapActions(viewModel, openAndPopUp) getSessionRecapActions(viewModel, clearAndNavigate)
) )
} }
@ -47,21 +58,88 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi
val sessionReport: SessionReport = sessionRecapActions.getSessionReport() val sessionReport: SessionReport = sessionRecapActions.getSessionReport()
val studyTime: Int = sessionReport.studyTime val studyTime: Int = sessionReport.studyTime
val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS() val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS()
val (background1, setBackground1) = remember { mutableStateOf(Color.Transparent) }
val (background2, setBackground2) = remember { mutableStateOf(Color.Transparent) }
Column( Column(
modifier = modifier modifier = modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) { ) {
Text(text = "You studied: $hms") Text(
text = stringResource(R.string.congrats, hms),
modifier = Modifier
.fillMaxWidth(),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Light,
fontSize = 30.sp,
BasicButton( )
R.string.save, Modifier.basicButton()
Column(
modifier = Modifier.fillMaxWidth()
) { ) {
sessionRecapActions.saveSession() Text(
text = stringResource(R.string.how_did_it_go),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Light,
fontSize = 30.sp
)
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.CenterHorizontally)
) {
ImageBackgroundButton(
paint = painterResource(id = R.drawable.mood_1),
str = stringResource(id = R.string.good),
background2 = background2,
setBackground1 = setBackground2,
setBackground2 = setBackground1
)
ImageBackgroundButton(
paint = painterResource(id = R.drawable.mood_2),
str = stringResource(id = R.string.bad),
background2 = background1,
setBackground1 = setBackground1,
setBackground2 = setBackground2
)
}
} }
BasicButton(
R.string.discard, Modifier.basicButton(), Column {
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red) BasicButton(
) { R.string.save, Modifier.basicButton()
sessionRecapActions.discardSession() ) {
sessionRecapActions.saveSession()
}
BasicButton(
R.string.discard, Modifier.basicButton(),
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
) {
sessionRecapActions.discardSession()
}
} }
} }
} }
@Preview
@Composable
fun SessionRecapScreenPreview() {
SessionRecapScreen(
modifier = Modifier,
sessionRecapActions = SessionRecapActions(
{ SessionReport(
studyTime = 100,
) },
{},
{},
)
)
}

View file

@ -24,15 +24,15 @@ class SessionRecapViewModel @Inject constructor(
return selectedSessionReport() return selectedSessionReport()
} }
fun saveSession(open: (String, String) -> Unit) { fun saveSession(open: (String) -> Unit) {
sessionDAO.saveSession(getSessionReport()) sessionDAO.saveSession(getSessionReport())
val newTask = val newTask =
selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime) selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime)
taskDAO.updateTask(newTask) taskDAO.updateTask(newTask)
open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) open(StudeezDestinations.HOME_SCREEN)
} }
fun discardSession(open: (String, String) -> Unit) { fun discardSession(open: (String) -> Unit) {
open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) open(StudeezDestinations.HOME_SCREEN)
} }
} }

View file

@ -36,6 +36,8 @@ fun SubjectRoute(
navigationBarActions = navigationBarActions, navigationBarActions = navigationBarActions,
onAddSubject = { viewModel.onAddSubject(open) }, onAddSubject = { viewModel.onAddSubject(open) },
onViewSubject = { viewModel.onViewSubject(it, open) }, onViewSubject = { viewModel.onViewSubject(it, open) },
getTaskCount = viewModel::getTaskCount,
getCompletedTaskCount = viewModel::getCompletedTaskCount,
getStudyTime = viewModel::getStudyTime, getStudyTime = viewModel::getStudyTime,
uiState, uiState,
) )
@ -47,6 +49,8 @@ fun SubjectScreen(
navigationBarActions: NavigationBarActions, navigationBarActions: NavigationBarActions,
onAddSubject: () -> Unit, onAddSubject: () -> Unit,
onViewSubject: (Subject) -> Unit, onViewSubject: (Subject) -> Unit,
getTaskCount: (Subject) -> Flow<Int>,
getCompletedTaskCount: (Subject) -> Flow<Int>,
getStudyTime: (Subject) -> Flow<Int>, getStudyTime: (Subject) -> Flow<Int>,
uiState: SubjectUiState, uiState: SubjectUiState,
) { ) {
@ -76,6 +80,8 @@ fun SubjectScreen(
SubjectEntry( SubjectEntry(
subject = it, subject = it,
onViewSubject = { onViewSubject(it) }, onViewSubject = { onViewSubject(it) },
getTaskCount = { getTaskCount(it) },
getCompletedTaskCount = { getCompletedTaskCount(it) },
getStudyTime = { getStudyTime(it) }, getStudyTime = { getStudyTime(it) },
) )
} }
@ -94,13 +100,14 @@ fun SubjectScreenPreview() {
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
onAddSubject = {}, onAddSubject = {},
onViewSubject = {}, onViewSubject = {},
getTaskCount = { flowOf() },
getCompletedTaskCount = { flowOf() },
getStudyTime = { flowOf() }, getStudyTime = { flowOf() },
uiState = SubjectUiState.Succes( uiState = SubjectUiState.Succes(
listOf( listOf(
Subject( Subject(
name = "Test Subject", name = "Test Subject",
argb_color = 0xFFFFD200, argb_color = 0xFFFFD200,
taskCount = 5, taskCompletedCount = 2,
) )
) )
) )
@ -115,7 +122,9 @@ fun SubjectScreenLoadingPreview() {
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
onAddSubject = {}, onAddSubject = {},
onViewSubject = {}, onViewSubject = {},
getTaskCount = { flowOf() },
getCompletedTaskCount = { flowOf() },
getStudyTime = { flowOf() }, getStudyTime = { flowOf() },
uiState = SubjectUiState.Loading uiState = SubjectUiState.Loading,
) )
} }

View file

@ -30,6 +30,14 @@ class SubjectViewModel @Inject constructor(
open(StudeezDestinations.ADD_SUBJECT_FORM) 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> { fun getStudyTime(subject: Subject): Flow<Int> {
return subjectDAO.getStudyTime(subject) return subjectDAO.getStudyTime(subject)
} }

View file

@ -2,20 +2,28 @@ package be.ugent.sel.studeez.screens.subjects.form
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
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.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
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.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.DeleteButton 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.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.common.ext.generateRandomArgb
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
import kotlinx.coroutines.launch
import be.ugent.sel.studeez.R.string as AppText import be.ugent.sel.studeez.R.string as AppText
@Composable @Composable
@ -31,7 +39,7 @@ fun SubjectCreateRoute(
uiState = uiState, uiState = uiState,
onConfirm = { viewModel.onCreate(openAndPopUp) }, onConfirm = { viewModel.onCreate(openAndPopUp) },
onNameChange = viewModel::onNameChange, onNameChange = viewModel::onNameChange,
onColorChange = {}, onColorChange = viewModel::onColorChange,
) )
} }
@ -42,16 +50,19 @@ fun SubjectEditRoute(
viewModel: SubjectEditFormViewModel, viewModel: SubjectEditFormViewModel,
) { ) {
val uiState by viewModel.uiState val uiState by viewModel.uiState
val coroutineScope = rememberCoroutineScope()
SubjectForm( SubjectForm(
title = AppText.edit_subject, title = AppText.edit_subject,
goBack = goBack, goBack = goBack,
uiState = uiState, uiState = uiState,
onConfirm = { viewModel.onEdit(openAndPopUp) }, onConfirm = { viewModel.onEdit(openAndPopUp) },
onNameChange = viewModel::onNameChange, onNameChange = viewModel::onNameChange,
onColorChange = {}, onColorChange = viewModel::onColorChange,
) { ) {
DeleteButton(text = AppText.delete_subject) { DeleteButton(text = AppText.delete_subject) {
viewModel.onDelete(openAndPopUp) coroutineScope.launch {
viewModel.onDelete(openAndPopUp)
}
} }
} }
} }
@ -63,21 +74,21 @@ fun SubjectForm(
uiState: SubjectFormUiState, uiState: SubjectFormUiState,
onConfirm: () -> Unit, onConfirm: () -> Unit,
onNameChange: (String) -> Unit, onNameChange: (String) -> Unit,
onColorChange: (Color) -> Unit, onColorChange: (Long) -> Unit,
extraButton: @Composable () -> Unit = {}, extraButton: @Composable () -> Unit = {},
) { ) {
SecondaryScreenTemplate( FormComposable(
title = resources().getString(title), title = resources().getString(title),
popUp = goBack, popUp = goBack,
) { ) {
Column { Column {
OutlinedTextField( LabelledInputField(
singleLine = true, singleLine = true,
value = uiState.name, value = uiState.name,
onValueChange = onNameChange, onNewValue = onNameChange,
placeholder = { Text(stringResource(id = AppText.name)) }, label = AppText.name,
modifier = Modifier.fieldModifier(),
) )
ColorPicker(onColorChange, uiState)
BasicButton( BasicButton(
text = AppText.confirm, text = AppText.confirm,
modifier = Modifier.basicButton(), 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 @Preview
@Composable @Composable
fun AddSubjectFormPreview() { fun AddSubjectFormPreview() {

View file

@ -1,6 +1,9 @@
package be.ugent.sel.studeez.screens.subjects.form 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( data class SubjectFormUiState(
val name: String = "", val name: String = "",
val color: Long = 0xFFFFD200, val color: Long = Color.generateRandomArgb(),
) )

View file

@ -2,10 +2,13 @@ package be.ugent.sel.studeez.screens.subjects.form
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf 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.SelectedSubject
import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.SubjectDAO 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.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -59,6 +62,7 @@ class SubjectCreateFormViewModel @Inject constructor(
@HiltViewModel @HiltViewModel
class SubjectEditFormViewModel @Inject constructor( class SubjectEditFormViewModel @Inject constructor(
subjectDAO: SubjectDAO, subjectDAO: SubjectDAO,
private val taskDAO: TaskDAO,
selectedSubject: SelectedSubject, selectedSubject: SelectedSubject,
logService: LogService, logService: LogService,
) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) { ) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) {
@ -69,17 +73,19 @@ class SubjectEditFormViewModel @Inject constructor(
) )
) )
fun onDelete(openAndPopUp: (String, String) -> Unit) { suspend fun onDelete(openAndPopUp: (String, String) -> Unit) {
subjectDAO.updateSubject(selectedSubject().copy(archived = true)) subjectDAO.archiveSubject(selectedSubject())
openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
} }
fun onEdit(openAndPopUp: (String, String) -> Unit) { fun onEdit(openAndPopUp: (String, String) -> Unit) {
val newSubject = selectedSubject().copy( selectedSubject.set(
name = name, selectedSubject().copy(
argb_color = color, name = name,
argb_color = color,
)
) )
subjectDAO.updateSubject(newSubject) subjectDAO.updateSubject(selectedSubject())
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
} }
} }

View file

@ -11,7 +11,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.DeleteButton 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.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.resources
@ -62,7 +62,7 @@ fun TaskForm(
onNameChange: (String) -> Unit, onNameChange: (String) -> Unit,
extraButton: @Composable () -> Unit = {} extraButton: @Composable () -> Unit = {}
) { ) {
SecondaryScreenTemplate( FormComposable(
title = resources().getString(title), title = resources().getString(title),
popUp = goBack, popUp = goBack,
) { ) {

View file

@ -1,42 +0,0 @@
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.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.R.string as AppText
@Composable
fun TimerAddRoute(
popUp: () -> Unit,
viewModel: TimerFormViewModel
) {
TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.add_timer) {
viewModel.saveTimer(it, goBack = popUp)
}
}
@Composable
fun TimerEditRoute(
popUp: () -> Unit,
viewModel: TimerFormViewModel
) {
TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.edit_timer) {
viewModel.editTimer(it, goBack = popUp)
}
}
@Composable
fun TimerFormScreen(
popUp: () -> Unit,
getTimerInfo: () -> TimerInfo,
@StringRes label: Int,
onConfirmClick: (TimerInfo) -> Unit
) {
val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen())
SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) {
timerFormScreen(onConfirmClick)
}
}

View file

@ -0,0 +1,69 @@
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.DeleteButton
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
@Composable
fun TimerAddRoute(
popUp: () -> Unit,
viewModel: TimerFormViewModel
) {
TimerFormScreen(
popUp = popUp,
getTimerInfo = viewModel::getTimerInfo,
extraButton= { },
AppText.add_timer
) {
viewModel.saveTimer(it, goBack = {popUp(); popUp()})
}
}
@Composable
fun TimerEditRoute(
popUp: () -> Unit,
viewModel: TimerFormViewModel
) {
@Composable
fun deleteButton() {
DeleteButton(text = AppText.delete_timer) {
viewModel.deleteTimer(viewModel.getTimerInfo(), popUp)
}
}
TimerFormScreen(
popUp = popUp,
getTimerInfo = viewModel::getTimerInfo,
extraButton= { deleteButton() },
AppText.edit_timer
) {
viewModel.editTimer(it, goBack = popUp)
}
}
@Composable
fun TimerFormScreen(
popUp: () -> Unit,
getTimerInfo: () -> TimerInfo,
extraButton: @Composable () -> Unit,
@StringRes label: Int,
onConfirmClick: (TimerInfo) -> Unit
) {
val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen())
FormComposable(
title = stringResource(id = label),
popUp = popUp
) {
timerFormScreen(onConfirmClick, extraButton)
}
}

View file

@ -23,6 +23,11 @@ class TimerFormViewModel @Inject constructor(
goBack() goBack()
} }
fun deleteTimer(timerInfo: TimerInfo, goBack: () -> Unit) {
timerDAO.deleteTimer(timerInfo)
goBack()
}
fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) { fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) {
timerDAO.saveTimer(timerInfo) timerDAO.saveTimer(timerInfo)
goBack() goBack()

View file

@ -1,69 +1,84 @@
package be.ugent.sel.studeez.screens.timer_form.form_screens 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.Column
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.runtime.*
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.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
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.BasicButton
import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.composable.LabeledErrorTextField
import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.snackbar.SnackbarManager
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.R.string as AppText import be.ugent.sel.studeez.R.string as AppText
abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) {
protected val valids = mutableMapOf(
"name" to mutableStateOf(textPredicate(timerInfo.name)),
"description" to mutableStateOf(textPredicate(timerInfo.description))
)
protected val firsts = mutableMapOf(
"name" to mutableStateOf(true),
"description" to mutableStateOf(true)
)
@Composable @Composable
operator fun invoke(onSaveClick: (TimerInfo) -> Unit) { operator fun invoke(
onSaveClick: (TimerInfo) -> Unit,
extraButton: @Composable () -> Unit = {},
) {
var name by remember { mutableStateOf(timerInfo.name) } Column {
var description by remember { mutableStateOf(timerInfo.description) }
// This shall rerun whenever name and description change
timerInfo.name = name
timerInfo.description = description
Column(
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxHeight().verticalScroll(rememberScrollState()),
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
// Fields that every timer shares (ommited id)
LabelledInputField(
value = name,
onNewValue = { name = it },
label = R.string.name
)
LabelledInputField(
value = description,
onNewValue = { description = it },
label = AppText.description,
singleLine = false
)
ExtraFields()
// Fields that every timer shares (ommited id)
LabeledErrorTextField(
initialValue = timerInfo.name,
label = R.string.name,
errorText = AppText.name_error,
isValid = valids.getValue("name"),
isFirst = firsts.getValue("name"),
keyboardType = KeyboardType.Text,
predicate = { it.isNotBlank() }
) { correctName ->
timerInfo.name = correctName
} }
LabeledErrorTextField(
initialValue = timerInfo.description,
label = R.string.description,
errorText = AppText.description_error,
isValid = valids.getValue("description"),
isFirst = firsts.getValue("description"),
singleLine = false,
keyboardType = KeyboardType.Text,
predicate = { textPredicate(it) }
) { correctName ->
timerInfo.description = correctName
}
ExtraFields()
BasicButton(R.string.save, Modifier.basicButton()) { BasicButton(R.string.save, Modifier.basicButton()) {
onSaveClick(timerInfo) if (valids.all { it.component2().value }) { // All fields are valid
onSaveClick(timerInfo)
} else {
firsts.map {
it.component2().value = false
} // dont mask error because its not been filled out yet
SnackbarManager.showMessage(AppText.fill_out_error)
}
} }
extraButton()
} }
} }
private fun textPredicate(text: String): Boolean {
return text.isNotBlank()
}
@Composable @Composable
open fun ExtraFields() { open fun ExtraFields() {
// By default no extra fields, unless overwritten by subclass. // By default no extra fields, unless overwritten by subclass.

View file

@ -15,6 +15,8 @@ class BreakTimerFormScreen(
private val breakTimerInfo: PomodoroTimerInfo private val breakTimerInfo: PomodoroTimerInfo
): AbstractTimerFormScreen(breakTimerInfo) { ): AbstractTimerFormScreen(breakTimerInfo) {
@Composable @Composable
override fun ExtraFields() { override fun ExtraFields() {
// If the user presses the OK button on the timepicker, the time in the button should change // If the user presses the OK button on the timepicker, the time in the button should change
@ -26,12 +28,17 @@ class BreakTimerFormScreen(
breakTimerInfo.breakTime = newTime breakTimerInfo.breakTime = newTime
} }
valids["repeats"] = remember {mutableStateOf(true)}
firsts["repeats"] = remember { mutableStateOf(true) }
LabeledErrorTextField( LabeledErrorTextField(
initialValue = breakTimerInfo.repeats.toString(), initialValue = breakTimerInfo.repeats.toString(),
label = R.string.repeats, label = R.string.repeats,
errorText = AppText.repeats_error, errorText = AppText.repeats_error,
isValid = valids.getValue("repeats"),
isFirst = firsts.getValue("repeats"),
keyboardType = KeyboardType.Decimal, keyboardType = KeyboardType.Decimal,
predicate = { it.matches(Regex("[1-9]+\\d*")) } predicate = { isNumber(it) }
) { correctlyTypedInt -> ) { correctlyTypedInt ->
breakTimerInfo.repeats = correctlyTypedInt.toInt() breakTimerInfo.repeats = correctlyTypedInt.toInt()
} }
@ -39,6 +46,10 @@ class BreakTimerFormScreen(
} }
} }
fun isNumber(text: String): Boolean {
return text.matches(Regex("[1-9]+\\d*"))
}
@Preview @Preview
@Composable @Composable
fun BreakEditScreenPreview() { fun BreakEditScreenPreview() {

View file

@ -1,13 +1,13 @@
package be.ugent.sel.studeez.screens.timer_form.timer_type_select package be.ugent.sel.studeez.screens.timer_form.timer_type_select
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Button import androidx.compose.material.Button
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.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.data.local.models.timer_info.* import be.ugent.sel.studeez.data.local.models.timer_info.*
@ -37,7 +37,10 @@ fun TimerTypeSelectScreen(
) { ) {
TimerType.values().forEach { timerType -> TimerType.values().forEach { timerType ->
val default: TimerInfo = defaultTimerInfo.getValue(timerType) val default: TimerInfo = defaultTimerInfo.getValue(timerType)
Button(onClick = { viewModel.onTimerTypeChosen(default, open) }) { Button(
onClick = { viewModel.onTimerTypeChosen(default, open) },
modifier = Modifier.fillMaxWidth().padding(5.dp)
) {
Text(text = timerType.name) Text(text = timerType.name)
} }
} }

View file

@ -0,0 +1,5 @@
<vector android:height="75dp" android:tint="#999999"
android:viewportHeight="24" android:viewportWidth="24"
android:width="75dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,17.5c2.33,0 4.31,-1.46 5.11,-3.5L6.89,14c0.8,2.04 2.78,3.5 5.11,3.5z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="75dp" android:tint="#999999"
android:viewportHeight="24" android:viewportWidth="24"
android:width="75dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,14c-2.33,0 -4.31,1.46 -5.11,3.5h10.22c-0.8,-2.04 -2.78,-3.5 -5.11,-3.5z"/>
</vector>

View file

@ -46,6 +46,7 @@
<string name="delete_subject">Delete Subject</string> <string name="delete_subject">Delete Subject</string>
<string name="delete_task">Delete Task</string> <string name="delete_task">Delete Task</string>
<string name="view_tasks">View</string> <string name="view_tasks">View</string>
<string name="regenerate_color">Regenerate Color</string>
<!-- Sessions --> <!-- 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. --> <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. -->
@ -69,8 +70,15 @@
<!-- Timers --> <!-- Timers -->
<string name="timers">Timers</string> <string name="timers">Timers</string>
<string name="delete_timer">Delete Timer</string>
<string name="edit">Edit</string> <string name="edit">Edit</string>
<string name="add_timer">Add timer</string> <string name="add_timer">Add timer</string>
<string name="name_error">Name should not be blank</string>
<string name="description_error">Description should not be blank</string>
<string name="fill_out_error">Fill out all the fields correctly!</string>
<string name="pick_time">Select time</string> <string name="pick_time">Select time</string>
<string name="state_focus">Focus!</string> <string name="state_focus">Focus!</string>
<plurals name="state_focus_remaining"> <plurals name="state_focus_remaining">
@ -149,4 +157,11 @@
<string name="breakTime">Break Time</string> <string name="breakTime">Break Time</string>
<string name="repeats">Number of Repeats</string> <string name="repeats">Number of Repeats</string>
<!-- Session Recap -->
<string name="congrats">"Congratulations! You studied: %s"</string>
<string name="how_did_it_go">How did it go?</string>
<string name="good">Good</string>
<string name="bad">Bad</string>
</resources> </resources>