solve merge issues

This commit is contained in:
lbarraga 2023-05-15 21:43:02 +02:00
commit 61f6e2ac25
26 changed files with 364 additions and 154 deletions

1
.idea/misc.xml generated
View file

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<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-perf-ktx'
implementation 'com.google.firebase:firebase-config-ktx'
// Colorpicker
implementation 'com.github.skydoves:colorpicker-compose:1.0.2'
}
// 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.updateTransition
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.material.FloatingActionButton
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

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

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 argb_color: Long = 0,
var archived: Boolean = false,
@get:Exclude @set:Exclude
var taskCount: Int = 0,
@get:Exclude @set:Exclude
var taskCompletedCount: Int = 0,
)
object SubjectDocument {

View file

@ -6,14 +6,13 @@ class FunctionalPomodoroTimer(
val repeats: Int
) : FunctionalTimer(studyTime) {
var breaksRemaining = repeats
var breaksRemaining = repeats - 1
var isInBreak = false
override fun tick() {
if (hasEnded()) {
return
}
if (hasCurrentCountdownEnded()) {
if (isInBreak) {
breaksRemaining--

View file

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

View file

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

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

View file

@ -4,18 +4,13 @@ import android.media.MediaPlayer
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.R.string as AppText
@ -23,7 +18,7 @@ import be.ugent.sel.studeez.R.string as AppText
class BreakSessionScreen(
private val funPomoDoroTimer: FunctionalPomodoroTimer,
private var mediaplayer: MediaPlayer?
): AbstractSessionScreen() {
) : AbstractSessionScreen() {
@Composable
override fun MidSection() {
@ -37,23 +32,31 @@ class BreakSessionScreen(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) {
Dot(color = Color.DarkGray)
}
if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray)
repeat(funPomoDoroTimer.breaksRemaining - 1) {
Dot(color = Color.Gray)
if (funPomoDoroTimer.hasEnded()) {
repeat(funPomoDoroTimer.repeats) {
Dot(Color.Green)
}
} else {
repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining - 1) {
Dot(color = Color.DarkGray)
}
if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray)
repeat(funPomoDoroTimer.breaksRemaining) {
Dot(color = Color.Gray)
}
}
}
}
@Composable
private fun Dot(color: Color) {
Box(modifier = Modifier
.padding(5.dp)
.size(10.dp)
.clip(CircleShape)
.background(color))
Box(
modifier = Modifier
.padding(5.dp)
.size(10.dp)
.clip(CircleShape)
.background(color)
)
}
@Composable

View file

@ -1,13 +1,24 @@
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.Text
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.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.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.ImageBackgroundButton
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.timer_functional.HoursMinutesSeconds
@ -21,24 +32,24 @@ data class SessionRecapActions(
fun getSessionRecapActions(
viewModel: SessionRecapViewModel,
openAndPopUp: (String, String) -> Unit,
clearAndNavigate: (String) -> Unit,
): SessionRecapActions {
return SessionRecapActions(
viewModel::getSessionReport,
{viewModel.saveSession(openAndPopUp)},
{viewModel.discardSession(openAndPopUp)}
{ viewModel.saveSession(clearAndNavigate) },
{ viewModel.discardSession(clearAndNavigate) }
)
}
@Composable
fun SessionRecapRoute(
openAndPopUp: (String, String) -> Unit,
clearAndNavigate: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: SessionRecapViewModel,
) {
SessionRecapScreen(
modifier = modifier,
getSessionRecapActions(viewModel, openAndPopUp)
getSessionRecapActions(viewModel, clearAndNavigate)
)
}
@ -47,21 +58,88 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi
val sessionReport: SessionReport = sessionRecapActions.getSessionReport()
val studyTime: Int = sessionReport.studyTime
val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS()
val (background1, setBackground1) = remember { mutableStateOf(Color.Transparent) }
val (background2, setBackground2) = remember { mutableStateOf(Color.Transparent) }
Column(
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(),
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red)
) {
sessionRecapActions.discardSession()
Column {
BasicButton(
R.string.save, Modifier.basicButton()
) {
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()
}
fun saveSession(open: (String, String) -> Unit) {
fun saveSession(open: (String) -> Unit) {
sessionDAO.saveSession(getSessionReport())
val newTask =
selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime)
taskDAO.updateTask(newTask)
open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP)
open(StudeezDestinations.HOME_SCREEN)
}
fun discardSession(open: (String, String) -> Unit) {
open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP)
fun discardSession(open: (String) -> Unit) {
open(StudeezDestinations.HOME_SCREEN)
}
}

View file

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

View file

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

View file

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

View file

@ -1,6 +1,9 @@
package be.ugent.sel.studeez.screens.subjects.form
import androidx.compose.ui.graphics.Color
import be.ugent.sel.studeez.common.ext.generateRandomArgb
data class SubjectFormUiState(
val name: String = "",
val color: Long = 0xFFFFD200,
val color: Long = Color.generateRandomArgb(),
)

View file

@ -2,10 +2,13 @@ package be.ugent.sel.studeez.screens.subjects.form
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.Color
import be.ugent.sel.studeez.common.ext.generateRandomArgb
import be.ugent.sel.studeez.data.SelectedSubject
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.SubjectDAO
import be.ugent.sel.studeez.domain.TaskDAO
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
@ -59,6 +62,7 @@ class SubjectCreateFormViewModel @Inject constructor(
@HiltViewModel
class SubjectEditFormViewModel @Inject constructor(
subjectDAO: SubjectDAO,
private val taskDAO: TaskDAO,
selectedSubject: SelectedSubject,
logService: LogService,
) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) {
@ -69,17 +73,19 @@ class SubjectEditFormViewModel @Inject constructor(
)
)
fun onDelete(openAndPopUp: (String, String) -> Unit) {
subjectDAO.updateSubject(selectedSubject().copy(archived = true))
suspend fun onDelete(openAndPopUp: (String, String) -> Unit) {
subjectDAO.archiveSubject(selectedSubject())
openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
}
fun onEdit(openAndPopUp: (String, String) -> Unit) {
val newSubject = selectedSubject().copy(
name = name,
argb_color = color,
selectedSubject.set(
selectedSubject().copy(
name = name,
argb_color = color,
)
)
subjectDAO.updateSubject(newSubject)
subjectDAO.updateSubject(selectedSubject())
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 be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.DeleteButton
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.common.composable.FormComposable
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources
@ -62,7 +62,7 @@ fun TaskForm(
onNameChange: (String) -> Unit,
extraButton: @Composable () -> Unit = {}
) {
SecondaryScreenTemplate(
FormComposable(
title = resources().getString(title),
popUp = goBack,
) {

View file

@ -5,6 +5,7 @@ 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
@ -59,7 +60,10 @@ fun TimerFormScreen(
) {
val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen())
SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) {
FormComposable(
title = stringResource(id = label),
popUp = popUp
) {
timerFormScreen(onConfirmClick, extraButton)
}
}

View file

@ -1,13 +1,7 @@
package be.ugent.sel.studeez.screens.timer_form.form_screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import be.ugent.sel.studeez.R
@ -37,58 +31,47 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) {
extraButton: @Composable () -> Unit = {},
) {
Column(
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState()),
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 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()
Column {
// 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
}
Column {
BasicButton(R.string.save, Modifier.basicButton()) {
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()
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()) {
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()
}
}

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_task">Delete Task</string>
<string name="view_tasks">View</string>
<string name="regenerate_color">Regenerate Color</string>
<!-- Sessions -->
<string name="sessions_temp_description">Looks like you found the sessions screen! In here, your upcoming studying sessions with friends will be listed. You can accept invites or edit your own.</string> <!-- TODO Remove this description line once implemented. -->
@ -156,4 +157,11 @@
<string name="breakTime">Break Time</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>