resolve conflicts and merge

This commit is contained in:
Lukas Barragan Torres 2023-05-05 11:02:18 +02:00
commit 13b9c0591f
38 changed files with 1291 additions and 64 deletions

View file

@ -42,6 +42,5 @@
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="TestFunctionName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
</profile>
</component>

2
.idea/misc.xml generated
View file

@ -1,7 +1,7 @@
<?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="Android Studio default JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -124,6 +124,8 @@ dependencies {
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

@ -32,6 +32,12 @@ import be.ugent.sel.studeez.screens.sessions.SessionsRoute
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.tasks.SubjectRoute
import be.ugent.sel.studeez.screens.tasks.TaskRoute
import be.ugent.sel.studeez.screens.tasks.forms.SubjectAddRoute
import be.ugent.sel.studeez.screens.tasks.forms.SubjectEditRoute
import be.ugent.sel.studeez.screens.tasks.forms.TaskAddRoute
import be.ugent.sel.studeez.screens.tasks.forms.TaskEditRoute
import be.ugent.sel.studeez.screens.timer_form.timer_type_select.TimerTypeSelectScreen
import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute
import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute
@ -118,10 +124,56 @@ fun StudeezNavGraph(
)
}
composable(StudeezDestinations.TASKS_SCREEN) {
// TODO
composable(StudeezDestinations.SUBJECT_SCREEN) {
SubjectRoute(
open = open,
viewModel = hiltViewModel(),
drawerActions = drawerActions,
navigationBarActions = navigationBarActions,
)
}
composable(StudeezDestinations.ADD_SUBJECT_FORM) {
SubjectAddRoute(
goBack = goBack,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}
composable(StudeezDestinations.EDIT_SUBJECT_FORM) {
SubjectEditRoute(
goBack = goBack,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}
composable(StudeezDestinations.TASKS_SCREEN) {
TaskRoute(
goBack = goBack,
open = open,
viewModel = hiltViewModel(),
)
}
composable(StudeezDestinations.ADD_TASK_FORM) {
TaskAddRoute(
goBack = goBack,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}
composable(StudeezDestinations.EDIT_TASK_FORM) {
TaskEditRoute(
goBack = goBack,
openAndPopUp = openAndPopUp,
viewModel = hiltViewModel(),
)
}
composable(StudeezDestinations.SESSIONS_SCREEN) {
SessionsRoute(
drawerActions = drawerActions,
@ -134,7 +186,7 @@ fun StudeezNavGraph(
open,
viewModel = hiltViewModel(),
drawerActions = drawerActions,
navigationBarActions = navigationBarActions
navigationBarActions = navigationBarActions,
)
}

View file

@ -2,17 +2,33 @@ package be.ugent.sel.studeez.common.composable
import androidx.annotation.StringRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.material.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
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 androidx.compose.ui.unit.sp
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.card
import be.ugent.sel.studeez.common.ext.defaultButtonShape
import be.ugent.sel.studeez.R.string as AppText
@Composable
fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) {
@ -29,7 +45,7 @@ fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit
@Composable
fun BasicButton(
@StringRes text: Int,
modifier: Modifier,
modifier: Modifier = Modifier,
colors: ButtonColors = ButtonDefaults.buttonColors(),
border: BorderStroke? = null,
onClick: () -> Unit,
@ -51,53 +67,58 @@ fun BasicButton(
@Preview
@Composable
fun BasicButtonPreview() {
BasicButton(text = R.string.add_timer, modifier = Modifier.basicButton()) {}
}
@Composable
fun NotInternationalisedButton(
text: String,
modifier: Modifier = Modifier,
colors: ButtonColors = ButtonDefaults.buttonColors(),
border: BorderStroke? = null,
onClick: () -> Unit
) {
Button(
onClick = onClick,
modifier = modifier,
shape = defaultButtonShape(),
colors = colors,
border = border
) {
Text(text = text)
}
BasicButton(text = AppText.add_timer, modifier = Modifier.basicButton()) {}
}
@Composable
fun StealthButton(
@StringRes text: Int,
modifier: Modifier = Modifier.card(),
onClick: () -> Unit,
) {
BasicButton(
text = text,
onClick = onClick,
modifier = Modifier.card(),
modifier = modifier,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.surface,
contentColor = MaterialTheme.colors.onSurface
contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f)
),
border = BorderStroke(1.dp, MaterialTheme.colors.onSurface)
border = BorderStroke(3.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f))
)
}
@Preview
@Composable
fun StealthButtonCardPreview() {
StealthButton(text = R.string.edit) {
StealthButton(text = AppText.edit) {
}
}
@Composable
fun DeleteButton(
@StringRes text: Int,
onClick: () -> Unit,
) {
BasicButton(
text = text,
modifier = Modifier.basicButton(),
onClick = onClick,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.error,
contentColor = MaterialTheme.colors.onSurface,
),
)
}
@Preview
@Composable
fun DeleteButtonPreview() {
DeleteButton(text = AppText.delete_subject) {}
}
@Composable
fun DialogConfirmButton(@StringRes text: Int, action: () -> Unit) {
Button(
@ -119,4 +140,40 @@ fun DialogCancelButton(@StringRes text: Int, action: () -> Unit) {
) {
Text(text = stringResource(text))
}
}
@Composable
fun NewTaskSubjectButton(
onClick: () -> Unit,
@StringRes text: Int,
) {
Button(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.padding(10.dp, 5.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f),
),
shape = RoundedCornerShape(2.dp),
border = BorderStroke(1.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)),
elevation = null,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(text = stringResource(id = text))
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(id = text))
}
}
}
@Preview
@Composable
fun NewTaskButtonPreview() {
NewTaskSubjectButton(onClick = {}, text = AppText.new_task)
}

View file

@ -15,7 +15,7 @@ import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.PROFILE_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.SESSIONS_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.TASKS_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.SUBJECT_SCREEN
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.ui.theme.StudeezTheme
import be.ugent.sel.studeez.R.string as AppText
@ -43,7 +43,6 @@ fun getNavigationBarActions(
isSelectedTab = { screen ->
screen == getCurrentScreen()
},
onHomeClick = {
navigationBarViewModel.onHomeClick(open)
},
@ -90,7 +89,7 @@ fun NavigationBar(
)
},
label = { Text(text = resources().getString(AppText.tasks)) },
selected = navigationBarActions.isSelectedTab(TASKS_SCREEN),
selected = navigationBarActions.isSelectedTab(SUBJECT_SCREEN),
onClick = navigationBarActions.onTasksClick
)

View file

@ -1,12 +1,11 @@
package be.ugent.sel.studeez.common.composable.navbar
import be.ugent.sel.studeez.common.snackbar.SnackbarManager
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.PROFILE_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.SESSIONS_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.TASKS_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.SUBJECT_SCREEN
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@ -14,7 +13,6 @@ import be.ugent.sel.studeez.R.string as AppText
@HiltViewModel
class NavigationBarViewModel @Inject constructor(
private val accountDAO: AccountDAO,
logService: LogService
) : StudeezViewModel(logService) {
@ -23,7 +21,7 @@ class NavigationBarViewModel @Inject constructor(
}
fun onTasksClick(open: (String) -> Unit) {
open(TASKS_SCREEN)
open(SUBJECT_SCREEN)
}
fun onSessionsClick(open: (String) -> Unit) {

View file

@ -0,0 +1,122 @@
package be.ugent.sel.studeez.common.composable.tasks
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.List
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.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.R.string as AppText
import be.ugent.sel.studeez.common.composable.StealthButton
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
@Composable
fun SubjectEntry(
subject: Subject,
onViewSubject: () -> Unit,
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp, vertical = 5.dp),
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(start = 10.dp)
.weight(3f)
) {
Box(
modifier = Modifier
.size(20.dp)
.clip(CircleShape)
.background(Color(subject.argb_color)),
)
Column(
verticalArrangement = Arrangement.spacedBy(0.dp)
) {
Text(
text = subject.name,
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = HoursMinutesSeconds(subject.time).toString(),
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(3.dp)
) {
Icon(
imageVector = Icons.Default.List,
contentDescription = stringResource(id = AppText.tasks)
)
Text(text = "0/0") // TODO
}
}
}
}
StealthButton(
text = AppText.view_tasks,
modifier = Modifier
.padding(start = 10.dp, end = 5.dp)
.weight(1f)
) {
onViewSubject()
}
}
}
}
@Preview
@Composable
fun SubjectEntryPreview() {
SubjectEntry(
subject = Subject(
name = "Test Subject",
argb_color = 0xFFFFD200,
time = 60
),
) {}
}
@Preview
@Composable
fun OverflowSubjectEntryPreview() {
SubjectEntry(
subject = Subject(
name = "Testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt",
argb_color = 0xFFFFD200,
time = 60
),
) {}
}

View file

@ -0,0 +1,139 @@
package be.ugent.sel.studeez.common.composable.tasks
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Card
import androidx.compose.material.Checkbox
import androidx.compose.material.CheckboxDefaults
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.StealthButton
import be.ugent.sel.studeez.data.local.models.task.Task
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
import be.ugent.sel.studeez.resources
@Composable
fun TaskEntry(
task: Task,
onCheckTask: (Boolean) -> Unit,
onDeleteTask: () -> Unit,
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp, vertical = 5.dp),
) {
val color = if (task.completed) Color.Gray else MaterialTheme.colors.onSurface
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(start = 10.dp)
.weight(22f),
) {
Checkbox(
checked = task.completed,
onCheckedChange = onCheckTask,
colors = CheckboxDefaults.colors(
checkedColor = Color.Gray,
uncheckedColor = MaterialTheme.colors.onSurface,
)
)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = task.name,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
color = color,
modifier = Modifier.weight(13f),
)
Text(
text = "${HoursMinutesSeconds(task.time)}",
color = color,
modifier = Modifier.weight(7f)
)
}
}
Box(modifier = Modifier.weight(7f)) {
if (task.completed) {
IconButton(
onClick = onDeleteTask,
modifier = Modifier
.padding(start = 20.dp)
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = resources().getString(R.string.delete_task),
)
}
} else {
StealthButton(
text = R.string.start,
modifier = Modifier
.padding(end = 5.dp),
) {
}
}
}
}
}
}
@Preview
@Composable
fun TaskEntryPreview() {
TaskEntry(
task = Task(
name = "Test Task",
completed = false,
),
{}, {},
)
}
@Preview
@Composable
fun CompletedTaskEntryPreview() {
TaskEntry(
task = Task(
name = "Test Task",
completed = true,
),
{}, {},
)
}
@Preview
@Composable
fun OverflowTaskEntryPreview() {
TaskEntry(
task = Task(
name = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkkkk",
completed = false,
),
{}, {},
)
}

View file

@ -0,0 +1,20 @@
package be.ugent.sel.studeez.data
import be.ugent.sel.studeez.data.local.models.task.Subject
import javax.inject.Inject
import javax.inject.Singleton
/**
* Used to communicate the selected subject from the subject overview other screens.
* Because this is a singleton-class the view-models of both screens observe the same data.
*/
@Singleton
class SelectedSubject @Inject constructor() {
private lateinit var subject: Subject
operator fun invoke() = subject
fun set(subject: Subject) {
this.subject = subject
}
fun isSet() = this::subject.isInitialized
}

View file

@ -0,0 +1,21 @@
package be.ugent.sel.studeez.data
import be.ugent.sel.studeez.data.local.models.task.Task
import javax.inject.Inject
import javax.inject.Singleton
/**
* Used to communicate the selected task from the task overview other screens.
* Because this is a singleton-class the view-models of both screens observe the same data.
*/
@Singleton
class SelectedTask @Inject constructor() {
private lateinit var task: Task
operator fun invoke() = task
fun set(task: Task) {
this.task = task
}
fun isSet() = this::task.isInitialized
}

View file

@ -7,4 +7,4 @@ data class SessionReport(
@DocumentId val id: String = "",
val studyTime: Int = 0,
val endTime: Timestamp = Timestamp(0, 0)
)
)

View file

@ -0,0 +1,10 @@
package be.ugent.sel.studeez.data.local.models.task
import com.google.firebase.firestore.DocumentId
data class Subject(
@DocumentId val id: String = "",
val name: String = "",
val time: Int = 0,
val argb_color: Long = 0,
)

View file

@ -0,0 +1,19 @@
package be.ugent.sel.studeez.data.local.models.task
import com.google.firebase.firestore.DocumentId
data class Task(
@DocumentId val id: String = "",
val name: String = "",
val completed: Boolean = false,
val time: Int = 0,
val subjectId: String = "",
)
object TaskDocument {
const val id = "id"
const val name = "name"
const val completed = "completed"
const val time = "time"
const val subjectId = "subjectId"
}

View file

@ -3,19 +3,19 @@ package be.ugent.sel.studeez.data.local.models.timer_functional
data class HoursMinutesSeconds(val hours: Int, val minutes: Int, val seconds: Int) {
constructor(sec: Int): this(
sec / (60 * 60),
(sec / (60)) % 60,
sec % 60
hours = sec / (60 * 60),
minutes = (sec / (60)) % 60,
seconds = sec % 60,
)
fun getTotalSeconds(): Int {
return hours * 60 * 60 + minutes * 60 + seconds
return (hours * 60 * 60) + (minutes * 60) + seconds
}
override fun toString(): String {
val hoursString = hours.toString().padStart(2, '0')
val minutesString = minutes.toString().padStart(2, '0')
val secondsString = seconds.toString().padStart(2, '0')
return "$hoursString : $minutesString : $secondsString"
return "$hoursString:$minutesString:$secondsString"
}
}

View file

@ -10,4 +10,4 @@ class Time(var time: Int) {
fun getAsHMS(): HoursMinutesSeconds {
return HoursMinutesSeconds(time)
}
}
}

View file

@ -10,15 +10,27 @@ import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
abstract class DatabaseModule {
@Binds abstract fun provideAccountDAO(impl: FirebaseAccountDAO): AccountDAO
@Binds
abstract fun provideAccountDAO(impl: FirebaseAccountDAO): AccountDAO
@Binds abstract fun provideUserDAO(impl: FirebaseUserDAO): UserDAO
@Binds
abstract fun provideUserDAO(impl: FirebaseUserDAO): UserDAO
@Binds abstract fun provideTimerDAO(impl: FirebaseTimerDAO): TimerDAO
@Binds
abstract fun provideTimerDAO(impl: FirebaseTimerDAO): TimerDAO
@Binds abstract fun provideLogService(impl: LogServiceImpl): LogService
@Binds
abstract fun provideLogService(impl: LogServiceImpl): LogService
@Binds abstract fun provideConfigurationService(impl: FirebaseConfigurationService): ConfigurationService
@Binds
abstract fun provideConfigurationService(impl: FirebaseConfigurationService): ConfigurationService
@Binds abstract fun provideSessionDAO(impl: FireBaseSessionDAO): SessionDAO
@Binds
abstract fun provideSessionDAO(impl: FireBaseSessionDAO): SessionDAO
@Binds
abstract fun provideSubjectDAO(impl: FireBaseSubjectDAO): SubjectDAO
@Binds
abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO
}

View file

@ -0,0 +1,15 @@
package be.ugent.sel.studeez.domain
import be.ugent.sel.studeez.data.local.models.task.Subject
import kotlinx.coroutines.flow.Flow
interface SubjectDAO {
fun getSubjects(): Flow<List<Subject>>
fun saveSubject(newSubject: Subject)
fun deleteSubject(oldSubject: Subject)
fun updateSubject(newSubject: Subject)
}

View file

@ -0,0 +1,18 @@
package be.ugent.sel.studeez.domain
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.data.local.models.task.Task
import kotlinx.coroutines.flow.Flow
interface TaskDAO {
fun getTasks(subject: Subject): Flow<List<Task>>
fun saveTask(newTask: Task)
fun updateTask(newTask: Task)
fun deleteTask(oldTask: Task)
fun toggleTaskCompleted(task: Task, completed: Boolean)
}

View file

@ -1,8 +1,9 @@
package be.ugent.sel.studeez.domain.implementation
object FirebaseCollectionRoutes {
object FireBaseCollections {
const val SESSION_COLLECTION = "sessions"
const val USER_COLLECTION = "users"
const val TIMER_COLLECTION = "timers"
const val SUBJECT_COLLECTION = "subjects"
const val TASK_COLLECTION = "tasks"
}

View file

@ -31,8 +31,7 @@ class FireBaseSessionDAO @Inject constructor(
}
private fun currentUserSessionsCollection(): CollectionReference =
firestore.collection(FirebaseCollectionRoutes.USER_COLLECTION)
firestore.collection(FireBaseCollections.USER_COLLECTION)
.document(auth.currentUserId)
.collection(FirebaseCollectionRoutes.SESSION_COLLECTION)
.collection(FireBaseCollections.SESSION_COLLECTION)
}

View file

@ -0,0 +1,39 @@
package be.ugent.sel.studeez.domain.implementation
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.SubjectDAO
import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.snapshots
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
class FireBaseSubjectDAO @Inject constructor(
private val firestore: FirebaseFirestore,
private val auth: AccountDAO,
) : SubjectDAO {
override fun getSubjects(): Flow<List<Subject>> {
return currentUserSubjectsCollection()
.snapshots()
.map { it.toObjects(Subject::class.java) }
}
override fun saveSubject(newSubject: Subject) {
currentUserSubjectsCollection().add(newSubject)
}
override fun deleteSubject(oldSubject: Subject) {
currentUserSubjectsCollection().document(oldSubject.id).delete()
}
override fun updateSubject(newSubject: Subject) {
currentUserSubjectsCollection().document(newSubject.id).set(newSubject)
}
private fun currentUserSubjectsCollection(): CollectionReference =
firestore.collection(FireBaseCollections.USER_COLLECTION)
.document(auth.currentUserId)
.collection(FireBaseCollections.SUBJECT_COLLECTION)
}

View file

@ -0,0 +1,49 @@
package be.ugent.sel.studeez.domain.implementation
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.data.local.models.task.Task
import be.ugent.sel.studeez.data.local.models.task.TaskDocument
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.TaskDAO
import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.snapshots
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
class FireBaseTaskDAO @Inject constructor(
private val firestore: FirebaseFirestore,
private val auth: AccountDAO,
) : TaskDAO {
override fun getTasks(subject: Subject): Flow<List<Task>> {
return selectedSubjectTasksCollection(subject.id)
.snapshots()
.map { it.toObjects(Task::class.java) }
}
override fun saveTask(newTask: Task) {
selectedSubjectTasksCollection(newTask.subjectId).add(newTask)
}
override fun updateTask(newTask: Task) {
selectedSubjectTasksCollection(newTask.id).document(newTask.id).set(newTask)
}
override fun deleteTask(oldTask: Task) {
selectedSubjectTasksCollection(oldTask.subjectId).document(oldTask.id).delete()
}
override fun toggleTaskCompleted(task: Task, completed: Boolean) {
selectedSubjectTasksCollection(task.subjectId)
.document(task.id)
.update(TaskDocument.completed, completed)
}
private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference =
firestore.collection(FireBaseCollections.USER_COLLECTION)
.document(auth.currentUserId)
.collection(FireBaseCollections.SUBJECT_COLLECTION)
.document(subjectId)
.collection(FireBaseCollections.TASK_COLLECTION)
}

View file

@ -1,11 +1,9 @@
package be.ugent.sel.studeez.domain.implementation
import be.ugent.sel.studeez.data.local.models.timer_info.*
import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.*
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.TimerDAO
import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.snapshots
import kotlinx.coroutines.flow.Flow
@ -50,8 +48,8 @@ class FirebaseTimerDAO @Inject constructor(
}
private fun currentUserTimersCollection(): CollectionReference =
firestore.collection(FirebaseCollectionRoutes.USER_COLLECTION)
firestore.collection(FireBaseCollections.USER_COLLECTION)
.document(auth.currentUserId)
.collection(FirebaseCollectionRoutes.TIMER_COLLECTION)
.collection(FireBaseCollections.TIMER_COLLECTION)
}

View file

@ -3,7 +3,7 @@ package be.ugent.sel.studeez.navigation
object StudeezDestinations {
// NavBar
const val HOME_SCREEN = "home"
const val TASKS_SCREEN = "tasks"
const val SUBJECT_SCREEN = "subjects"
const val SESSIONS_SCREEN = "sessions"
const val PROFILE_SCREEN = "profile"
@ -23,6 +23,12 @@ object StudeezDestinations {
const val SESSION_SCREEN = "session"
const val SESSION_RECAP = "session_recap"
const val ADD_SUBJECT_FORM = "add_subject"
const val EDIT_SUBJECT_FORM = "edit_subject"
const val TASKS_SCREEN = "tasks"
const val ADD_TASK_FORM = "add_task"
const val EDIT_TASK_FORM = "edit_task"
// Friends flow
const val SEARCH_FRIENDS_SCREEN = "search_friends"

View file

@ -9,7 +9,12 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.graphics.Color

View file

@ -0,0 +1,80 @@
package be.ugent.sel.studeez.screens.tasks
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
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.NewTaskSubjectButton
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
import be.ugent.sel.studeez.common.composable.tasks.SubjectEntry
import be.ugent.sel.studeez.data.local.models.task.Subject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import be.ugent.sel.studeez.R.string as AppText
@Composable
fun SubjectRoute(
open: (String) -> Unit,
viewModel: SubjectViewModel,
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
) {
SubjectScreen(
drawerActions = drawerActions,
navigationBarActions = navigationBarActions,
addSubject = { viewModel.addSubject(open) },
getSubjects = viewModel::getSubjects,
onViewSubject = { viewModel.onViewSubject(it, open) },
)
}
@Composable
fun SubjectScreen(
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
addSubject: () -> Unit,
getSubjects: () -> Flow<List<Subject>>,
onViewSubject: (Subject) -> Unit,
) {
PrimaryScreenTemplate(
title = stringResource(AppText.my_subjects),
drawerActions = drawerActions,
navigationBarActions = navigationBarActions,
barAction = {},
) {
val subjects = getSubjects().collectAsState(initial = emptyList())
Column(
modifier = Modifier.padding(top = 5.dp)
) {
LazyColumn {
items(subjects.value) {
SubjectEntry(
subject = it,
onViewSubject = { onViewSubject(it) },
)
}
}
NewTaskSubjectButton(onClick = addSubject, AppText.new_subject)
}
}
}
@Preview
@Composable
fun SubjectScreenPreview() {
SubjectScreen(
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
addSubject = {},
getSubjects = { flowOf() },
onViewSubject = {},
)
}

View file

@ -0,0 +1,31 @@
package be.ugent.sel.studeez.screens.tasks
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.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
@HiltViewModel
class SubjectViewModel @Inject constructor(
private val subjectDAO: SubjectDAO,
private val selectedSubject: SelectedSubject,
logService: LogService,
) : StudeezViewModel(logService) {
fun addSubject(open: (String) -> Unit) {
open(StudeezDestinations.ADD_SUBJECT_FORM)
}
fun getSubjects(): Flow<List<Subject>> {
return subjectDAO.getSubjects()
}
fun onViewSubject(subject: Subject, open: (String) -> Unit) {
selectedSubject.set(subject)
open(StudeezDestinations.TASKS_SCREEN)
}
}

View file

@ -0,0 +1,113 @@
package be.ugent.sel.studeez.screens.tasks
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
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.NewTaskSubjectButton
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.common.composable.tasks.TaskEntry
import be.ugent.sel.studeez.data.local.models.task.Subject
import be.ugent.sel.studeez.data.local.models.task.Task
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import be.ugent.sel.studeez.R.string as AppText
data class TaskActions(
val addTask: () -> Unit,
val getSubject: () -> Subject,
val getTasks: () -> Flow<List<Task>>,
val deleteTask: (Task) -> Unit,
val onCheckTask: (Task, Boolean) -> Unit,
val editSubject: () -> Unit,
)
fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions {
return TaskActions(
addTask = { viewModel.addTask(open) },
getTasks = viewModel::getTasks,
getSubject = viewModel::getSelectedSubject,
deleteTask = viewModel::deleteTask,
onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) },
editSubject = { viewModel.editSubject(open) }
)
}
@Composable
fun TaskRoute(
goBack: () -> Unit,
open: (String) -> Unit,
viewModel: TaskViewModel,
) {
TaskScreen(
goBack = goBack,
taskActions = getTaskActions(viewModel = viewModel, open = open),
)
}
@Composable
fun TaskScreen(
goBack: () -> Unit,
taskActions: TaskActions,
) {
SecondaryScreenTemplate(
title = taskActions.getSubject().name,
popUp = goBack,
barAction = { EditAction(onClick = taskActions.editSubject) }
) {
val tasks = taskActions.getTasks().collectAsState(initial = emptyList())
Column(
modifier = Modifier.padding(top = 5.dp)
) {
LazyColumn {
items(tasks.value) {
TaskEntry(
task = it,
onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) },
onDeleteTask = { taskActions.deleteTask(it) },
)
}
}
NewTaskSubjectButton(onClick = taskActions.addTask, AppText.new_task)
}
}
}
@Composable
fun EditAction(
onClick: () -> Unit
) {
IconButton(onClick = onClick) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(AppText.edit_task)
)
}
}
@Preview
@Composable
fun TaskScreenPreview() {
TaskScreen(
goBack = {},
taskActions = TaskActions(
{},
{ Subject(name = "Test Subject") },
{ flowOf() },
{},
{ _, _ -> run {} },
{},
)
)
}

View file

@ -0,0 +1,50 @@
package be.ugent.sel.studeez.screens.tasks
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.Task
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
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
@HiltViewModel
class TaskViewModel @Inject constructor(
private val taskDAO: TaskDAO,
private val subjectDAO: SubjectDAO,
private val selectedSubject: SelectedSubject,
logService: LogService,
) : StudeezViewModel(logService) {
fun addTask(open: (String) -> Unit) {
open(StudeezDestinations.ADD_TASK_FORM)
}
fun getTasks(): Flow<List<Task>> {
return taskDAO.getTasks(selectedSubject())
}
fun deleteSubject(open: (String) -> Unit) {
subjectDAO.deleteSubject(selectedSubject())
open(StudeezDestinations.SUBJECT_SCREEN)
}
fun getSelectedSubject(): Subject {
return selectedSubject()
}
fun deleteTask(task: Task) {
taskDAO.deleteTask(task)
}
fun toggleTaskCompleted(task: Task, completed: Boolean) {
taskDAO.toggleTaskCompleted(task, completed)
}
fun editSubject(open: (String) -> Unit) {
open(StudeezDestinations.EDIT_SUBJECT_FORM)
}
}

View file

@ -0,0 +1,119 @@
package be.ugent.sel.studeez.screens.tasks.forms
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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 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.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.R.string as AppText
@Composable
fun SubjectAddRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: SubjectFormViewModel,
) {
val uiState by viewModel.uiState
SubjectForm(
title = AppText.new_subject,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onCreate(openAndPopUp) },
onNameChange = viewModel::onNameChange,
onColorChange = {},
)
}
@Composable
fun SubjectEditRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: SubjectFormViewModel,
) {
val uiState by viewModel.uiState
SubjectForm(
title = AppText.edit_subject,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onEdit(openAndPopUp) },
onNameChange = viewModel::onNameChange,
onColorChange = {},
) {
DeleteButton(text = AppText.delete_subject) {
viewModel.onDelete(openAndPopUp)
}
}
}
@Composable
fun SubjectForm(
@StringRes title: Int,
goBack: () -> Unit,
uiState: SubjectFormUiState,
onConfirm: () -> Unit,
onNameChange: (String) -> Unit,
onColorChange: (Color) -> Unit,
extraButton: @Composable () -> Unit = {},
) {
SecondaryScreenTemplate(
title = resources().getString(title),
popUp = goBack,
) {
Column {
OutlinedTextField(
singleLine = true,
value = uiState.name,
onValueChange = onNameChange,
placeholder = { Text(stringResource(id = AppText.name)) },
modifier = Modifier.fieldModifier(),
)
BasicButton(
text = AppText.confirm,
modifier = Modifier.basicButton(),
onClick = onConfirm,
)
extraButton()
}
}
}
@Preview
@Composable
fun AddSubjectFormPreview() {
SubjectForm(
title = AppText.new_subject,
goBack = {},
uiState = SubjectFormUiState(),
onConfirm = {},
onNameChange = {},
onColorChange = {},
)
}
@Preview
@Composable
fun EditSubjectFormPreview() {
SubjectForm(
title = AppText.edit_subject,
goBack = {},
uiState = SubjectFormUiState(
name = "Test Subject",
),
onConfirm = {},
onNameChange = {},
onColorChange = {},
) {
DeleteButton(text = AppText.delete_subject) {}
}
}

View file

@ -0,0 +1,6 @@
package be.ugent.sel.studeez.screens.tasks.forms
data class SubjectFormUiState(
val name: String = "",
val color: Long = 0xFFFFD200,
)

View file

@ -0,0 +1,69 @@
package be.ugent.sel.studeez.screens.tasks.forms
import androidx.compose.runtime.mutableStateOf
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.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class SubjectFormViewModel @Inject constructor(
private val subjectDAO: SubjectDAO,
private val selectedSubject: SelectedSubject,
logService: LogService,
) : StudeezViewModel(logService) {
var uiState = mutableStateOf(
if (selectedSubject.isSet()) SubjectFormUiState(
name = selectedSubject().name,
color = selectedSubject().argb_color
)
else SubjectFormUiState()
)
private set
private val name: String
get() = uiState.value.name
private val color: Long
get() = uiState.value.color
fun onNameChange(newValue: String) {
uiState.value = uiState.value.copy(name = newValue)
}
fun onColorChange(newValue: Long) {
uiState.value = uiState.value.copy(color = newValue)
}
fun onDelete(openAndPopUp: (String, String) -> Unit) {
subjectDAO.deleteSubject(selectedSubject())
openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
}
fun onCreate(openAndPopUp: (String, String) -> Unit) {
val newSubject = Subject(
name = name,
argb_color = color,
)
subjectDAO.saveSubject(
newSubject
)
// TODO open newly created subject
// selectedSubject.set(newSubject)
// open(StudeezDestinations.TASKS_SCREEN)
openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.ADD_SUBJECT_FORM)
}
fun onEdit(openAndPopUp: (String, String) -> Unit) {
val newSubject = selectedSubject().copy(
name = name,
argb_color = color,
)
subjectDAO.updateSubject(newSubject)
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM)
}
}

View file

@ -0,0 +1,113 @@
package be.ugent.sel.studeez.screens.tasks.forms
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
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.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.R.string as AppText
@Composable
fun TaskAddRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TaskFormViewModel,
) {
val uiState by viewModel.uiState
TaskForm(
title = AppText.new_task,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onCreate(openAndPopUp) },
onNameChange = viewModel::onNameChange
)
}
@Composable
fun TaskEditRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TaskFormViewModel,
) {
val uiState by viewModel.uiState
TaskForm(
title = AppText.edit_task,
goBack = goBack,
uiState = uiState,
onConfirm = { viewModel.onEdit(openAndPopUp) },
onNameChange = viewModel::onNameChange
) {
DeleteButton(text = AppText.delete_task) {
viewModel.onDelete(openAndPopUp)
}
}
}
@Composable
fun TaskForm(
@StringRes title: Int,
goBack: () -> Unit,
uiState: TaskFormUiState,
onConfirm: () -> Unit,
onNameChange: (String) -> Unit,
extraButton: @Composable () -> Unit = {}
) {
SecondaryScreenTemplate(
title = resources().getString(title),
popUp = goBack,
) {
Column {
OutlinedTextField(
singleLine = true,
value = uiState.name,
onValueChange = onNameChange,
placeholder = { Text(stringResource(id = AppText.name)) },
modifier = Modifier.fieldModifier(),
)
BasicButton(
text = AppText.confirm,
modifier = Modifier.basicButton(),
onClick = onConfirm,
)
extraButton()
}
}
}
@Preview
@Composable
fun AddTaskFormPreview() {
TaskForm(
title = AppText.new_task,
goBack = {},
uiState = TaskFormUiState(),
onConfirm = {},
onNameChange = {},
)
}
@Preview
@Composable
fun EditTaskFormPreview() {
TaskForm(
title = AppText.edit_task,
goBack = {},
uiState = TaskFormUiState(
name = "Test Task",
),
onConfirm = {},
onNameChange = {},
) {
DeleteButton(text = AppText.delete_task) {}
}
}

View file

@ -0,0 +1,5 @@
package be.ugent.sel.studeez.screens.tasks.forms
data class TaskFormUiState(
val name: String = "",
)

View file

@ -0,0 +1,49 @@
package be.ugent.sel.studeez.screens.tasks.forms
import androidx.compose.runtime.mutableStateOf
import be.ugent.sel.studeez.data.SelectedSubject
import be.ugent.sel.studeez.data.SelectedTask
import be.ugent.sel.studeez.data.local.models.task.Task
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.TaskDAO
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class TaskFormViewModel @Inject constructor(
private val taskDAO: TaskDAO,
private val selectedSubject: SelectedSubject,
private val selectedTask: SelectedTask,
logService: LogService,
) : StudeezViewModel(logService) {
var uiState = mutableStateOf(
if (selectedTask.isSet()) TaskFormUiState(selectedTask().name) else TaskFormUiState()
)
private set
private val name: String
get() = uiState.value.name
fun onNameChange(newValue: String) {
uiState.value = uiState.value.copy(name = newValue)
}
fun onDelete(openAndPopUp: (String, String) -> Unit) {
taskDAO.deleteTask(selectedTask())
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM)
}
fun onCreate(openAndPopUp: (String, String) -> Unit) {
val newTask = Task(name = name, subjectId = selectedSubject().id)
taskDAO.saveTask(newTask)
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.ADD_TASK_FORM)
}
fun onEdit(openAndPopUp: (String, String) -> Unit) {
val newTask = Task(name = name)
taskDAO.updateTask(newTask)
openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM)
}
}

View file

@ -6,7 +6,11 @@ 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.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 be.ugent.sel.studeez.R

View file

@ -32,6 +32,14 @@
<!-- Tasks -->
<string name="tasks">Tasks</string>
<string name="task">Task</string>
<string name="my_subjects">My Subjects</string>
<string name="new_subject">New Subject</string>
<string name="new_task">New Task</string>
<string name="edit_subject">Edit Subject</string>
<string name="edit_task">Edit Task</string>
<string name="delete_subject">Delete Subject</string>
<string name="delete_task">Delete Task</string>
<string name="view_tasks">View</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. -->