resolve merge conflicts
This commit is contained in:
commit
924e136153
30 changed files with 469 additions and 184 deletions
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
|
@ -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">
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
|
@ -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),
|
||||||
|
|
|
@ -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() },
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -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 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 {
|
||||||
|
|
|
@ -13,7 +13,6 @@ class FunctionalPomodoroTimer(
|
||||||
if (hasEnded()) {
|
if (hasEnded()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCurrentCountdownEnded()) {
|
if (hasCurrentCountdownEnded()) {
|
||||||
if (isInBreak) {
|
if (isInBreak) {
|
||||||
breaksRemaining--
|
breaksRemaining--
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
) },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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(),
|
||||||
)
|
)
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
app/src/main/res/drawable/mood_1.xml
Normal file
5
app/src/main/res/drawable/mood_1.xml
Normal 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>
|
5
app/src/main/res/drawable/mood_2.xml
Normal file
5
app/src/main/res/drawable/mood_2.xml
Normal 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>
|
|
@ -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>
|
||||||
|
|
Reference in a new issue