resolve conflicts and merge
This commit is contained in:
commit
13b9c0591f
38 changed files with 1291 additions and 64 deletions
1
.idea/inspectionProfiles/Project_Default.xml
generated
1
.idea/inspectionProfiles/Project_Default.xml
generated
|
@ -42,6 +42,5 @@
|
||||||
<option name="processLiterals" value="true" />
|
<option name="processLiterals" value="true" />
|
||||||
<option name="processComments" value="true" />
|
<option name="processComments" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="TestFunctionName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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="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" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|
|
@ -124,6 +124,8 @@ dependencies {
|
||||||
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
|
||||||
|
|
|
@ -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.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.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.timer_type_select.TimerTypeSelectScreen
|
||||||
import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute
|
import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute
|
||||||
import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute
|
import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute
|
||||||
|
@ -118,10 +124,56 @@ fun StudeezNavGraph(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(StudeezDestinations.TASKS_SCREEN) {
|
composable(StudeezDestinations.SUBJECT_SCREEN) {
|
||||||
// TODO
|
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) {
|
composable(StudeezDestinations.SESSIONS_SCREEN) {
|
||||||
SessionsRoute(
|
SessionsRoute(
|
||||||
drawerActions = drawerActions,
|
drawerActions = drawerActions,
|
||||||
|
@ -134,7 +186,7 @@ fun StudeezNavGraph(
|
||||||
open,
|
open,
|
||||||
viewModel = hiltViewModel(),
|
viewModel = hiltViewModel(),
|
||||||
drawerActions = drawerActions,
|
drawerActions = drawerActions,
|
||||||
navigationBarActions = navigationBarActions
|
navigationBarActions = navigationBarActions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,33 @@ package be.ugent.sel.studeez.common.composable
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.BorderStroke
|
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.runtime.Composable
|
||||||
|
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.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 androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.basicButton
|
||||||
import be.ugent.sel.studeez.common.ext.card
|
import be.ugent.sel.studeez.common.ext.card
|
||||||
import be.ugent.sel.studeez.common.ext.defaultButtonShape
|
import be.ugent.sel.studeez.common.ext.defaultButtonShape
|
||||||
|
import be.ugent.sel.studeez.R.string as AppText
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) {
|
fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) {
|
||||||
|
@ -29,7 +45,7 @@ fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit
|
||||||
@Composable
|
@Composable
|
||||||
fun BasicButton(
|
fun BasicButton(
|
||||||
@StringRes text: Int,
|
@StringRes text: Int,
|
||||||
modifier: Modifier,
|
modifier: Modifier = Modifier,
|
||||||
colors: ButtonColors = ButtonDefaults.buttonColors(),
|
colors: ButtonColors = ButtonDefaults.buttonColors(),
|
||||||
border: BorderStroke? = null,
|
border: BorderStroke? = null,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
@ -51,53 +67,58 @@ fun BasicButton(
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun BasicButtonPreview() {
|
fun BasicButtonPreview() {
|
||||||
BasicButton(text = R.string.add_timer, modifier = Modifier.basicButton()) {}
|
BasicButton(text = AppText.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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StealthButton(
|
fun StealthButton(
|
||||||
@StringRes text: Int,
|
@StringRes text: Int,
|
||||||
|
modifier: Modifier = Modifier.card(),
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
BasicButton(
|
BasicButton(
|
||||||
text = text,
|
text = text,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = Modifier.card(),
|
modifier = modifier,
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
backgroundColor = MaterialTheme.colors.surface,
|
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
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun StealthButtonCardPreview() {
|
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
|
@Composable
|
||||||
fun DialogConfirmButton(@StringRes text: Int, action: () -> Unit) {
|
fun DialogConfirmButton(@StringRes text: Int, action: () -> Unit) {
|
||||||
Button(
|
Button(
|
||||||
|
@ -119,4 +140,40 @@ fun DialogCancelButton(@StringRes text: Int, action: () -> Unit) {
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(text))
|
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)
|
||||||
}
|
}
|
|
@ -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.HOME_SCREEN
|
||||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.PROFILE_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.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.resources
|
||||||
import be.ugent.sel.studeez.ui.theme.StudeezTheme
|
import be.ugent.sel.studeez.ui.theme.StudeezTheme
|
||||||
import be.ugent.sel.studeez.R.string as AppText
|
import be.ugent.sel.studeez.R.string as AppText
|
||||||
|
@ -43,7 +43,6 @@ fun getNavigationBarActions(
|
||||||
isSelectedTab = { screen ->
|
isSelectedTab = { screen ->
|
||||||
screen == getCurrentScreen()
|
screen == getCurrentScreen()
|
||||||
},
|
},
|
||||||
|
|
||||||
onHomeClick = {
|
onHomeClick = {
|
||||||
navigationBarViewModel.onHomeClick(open)
|
navigationBarViewModel.onHomeClick(open)
|
||||||
},
|
},
|
||||||
|
@ -90,7 +89,7 @@ fun NavigationBar(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
label = { Text(text = resources().getString(AppText.tasks)) },
|
label = { Text(text = resources().getString(AppText.tasks)) },
|
||||||
selected = navigationBarActions.isSelectedTab(TASKS_SCREEN),
|
selected = navigationBarActions.isSelectedTab(SUBJECT_SCREEN),
|
||||||
onClick = navigationBarActions.onTasksClick
|
onClick = navigationBarActions.onTasksClick
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package be.ugent.sel.studeez.common.composable.navbar
|
package be.ugent.sel.studeez.common.composable.navbar
|
||||||
|
|
||||||
import be.ugent.sel.studeez.common.snackbar.SnackbarManager
|
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.domain.LogService
|
||||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
|
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.PROFILE_SCREEN
|
||||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.SESSIONS_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 be.ugent.sel.studeez.screens.StudeezViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -14,7 +13,6 @@ import be.ugent.sel.studeez.R.string as AppText
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class NavigationBarViewModel @Inject constructor(
|
class NavigationBarViewModel @Inject constructor(
|
||||||
private val accountDAO: AccountDAO,
|
|
||||||
logService: LogService
|
logService: LogService
|
||||||
) : StudeezViewModel(logService) {
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
|
@ -23,7 +21,7 @@ class NavigationBarViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onTasksClick(open: (String) -> Unit) {
|
fun onTasksClick(open: (String) -> Unit) {
|
||||||
open(TASKS_SCREEN)
|
open(SUBJECT_SCREEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSessionsClick(open: (String) -> Unit) {
|
fun onSessionsClick(open: (String) -> Unit) {
|
||||||
|
|
|
@ -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
|
||||||
|
),
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
),
|
||||||
|
{}, {},
|
||||||
|
)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
21
app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt
Normal file
21
app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt
Normal 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
|
||||||
|
}
|
|
@ -7,4 +7,4 @@ data class SessionReport(
|
||||||
@DocumentId val id: String = "",
|
@DocumentId val id: String = "",
|
||||||
val studyTime: Int = 0,
|
val studyTime: Int = 0,
|
||||||
val endTime: Timestamp = Timestamp(0, 0)
|
val endTime: Timestamp = Timestamp(0, 0)
|
||||||
)
|
)
|
|
@ -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,
|
||||||
|
)
|
|
@ -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"
|
||||||
|
}
|
|
@ -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) {
|
data class HoursMinutesSeconds(val hours: Int, val minutes: Int, val seconds: Int) {
|
||||||
|
|
||||||
constructor(sec: Int): this(
|
constructor(sec: Int): this(
|
||||||
sec / (60 * 60),
|
hours = sec / (60 * 60),
|
||||||
(sec / (60)) % 60,
|
minutes = (sec / (60)) % 60,
|
||||||
sec % 60
|
seconds = sec % 60,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getTotalSeconds(): Int {
|
fun getTotalSeconds(): Int {
|
||||||
return hours * 60 * 60 + minutes * 60 + seconds
|
return (hours * 60 * 60) + (minutes * 60) + seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
val hoursString = hours.toString().padStart(2, '0')
|
val hoursString = hours.toString().padStart(2, '0')
|
||||||
val minutesString = minutes.toString().padStart(2, '0')
|
val minutesString = minutes.toString().padStart(2, '0')
|
||||||
val secondsString = seconds.toString().padStart(2, '0')
|
val secondsString = seconds.toString().padStart(2, '0')
|
||||||
return "$hoursString : $minutesString : $secondsString"
|
return "$hoursString:$minutesString:$secondsString"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,4 +10,4 @@ class Time(var time: Int) {
|
||||||
fun getAsHMS(): HoursMinutesSeconds {
|
fun getAsHMS(): HoursMinutesSeconds {
|
||||||
return HoursMinutesSeconds(time)
|
return HoursMinutesSeconds(time)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,15 +10,27 @@ import dagger.hilt.components.SingletonComponent
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
abstract class DatabaseModule {
|
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
|
||||||
}
|
}
|
15
app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt
Normal file
15
app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt
Normal 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)
|
||||||
|
}
|
18
app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt
Normal file
18
app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt
Normal 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)
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
package be.ugent.sel.studeez.domain.implementation
|
package be.ugent.sel.studeez.domain.implementation
|
||||||
|
|
||||||
object FirebaseCollectionRoutes {
|
object FireBaseCollections {
|
||||||
const val SESSION_COLLECTION = "sessions"
|
const val SESSION_COLLECTION = "sessions"
|
||||||
const val USER_COLLECTION = "users"
|
const val USER_COLLECTION = "users"
|
||||||
const val TIMER_COLLECTION = "timers"
|
const val TIMER_COLLECTION = "timers"
|
||||||
|
const val SUBJECT_COLLECTION = "subjects"
|
||||||
|
const val TASK_COLLECTION = "tasks"
|
||||||
}
|
}
|
|
@ -31,8 +31,7 @@ class FireBaseSessionDAO @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun currentUserSessionsCollection(): CollectionReference =
|
private fun currentUserSessionsCollection(): CollectionReference =
|
||||||
firestore.collection(FirebaseCollectionRoutes.USER_COLLECTION)
|
firestore.collection(FireBaseCollections.USER_COLLECTION)
|
||||||
.document(auth.currentUserId)
|
.document(auth.currentUserId)
|
||||||
.collection(FirebaseCollectionRoutes.SESSION_COLLECTION)
|
.collection(FireBaseCollections.SESSION_COLLECTION)
|
||||||
|
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -1,11 +1,9 @@
|
||||||
package be.ugent.sel.studeez.domain.implementation
|
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.*
|
||||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.*
|
|
||||||
import be.ugent.sel.studeez.domain.AccountDAO
|
import be.ugent.sel.studeez.domain.AccountDAO
|
||||||
import be.ugent.sel.studeez.domain.TimerDAO
|
import be.ugent.sel.studeez.domain.TimerDAO
|
||||||
import com.google.firebase.firestore.CollectionReference
|
import com.google.firebase.firestore.CollectionReference
|
||||||
import com.google.firebase.firestore.DocumentSnapshot
|
|
||||||
import com.google.firebase.firestore.FirebaseFirestore
|
import com.google.firebase.firestore.FirebaseFirestore
|
||||||
import com.google.firebase.firestore.ktx.snapshots
|
import com.google.firebase.firestore.ktx.snapshots
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
@ -50,8 +48,8 @@ class FirebaseTimerDAO @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun currentUserTimersCollection(): CollectionReference =
|
private fun currentUserTimersCollection(): CollectionReference =
|
||||||
firestore.collection(FirebaseCollectionRoutes.USER_COLLECTION)
|
firestore.collection(FireBaseCollections.USER_COLLECTION)
|
||||||
.document(auth.currentUserId)
|
.document(auth.currentUserId)
|
||||||
.collection(FirebaseCollectionRoutes.TIMER_COLLECTION)
|
.collection(FireBaseCollections.TIMER_COLLECTION)
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ package be.ugent.sel.studeez.navigation
|
||||||
object StudeezDestinations {
|
object StudeezDestinations {
|
||||||
// NavBar
|
// NavBar
|
||||||
const val HOME_SCREEN = "home"
|
const val HOME_SCREEN = "home"
|
||||||
const val TASKS_SCREEN = "tasks"
|
const val SUBJECT_SCREEN = "subjects"
|
||||||
const val SESSIONS_SCREEN = "sessions"
|
const val SESSIONS_SCREEN = "sessions"
|
||||||
const val PROFILE_SCREEN = "profile"
|
const val PROFILE_SCREEN = "profile"
|
||||||
|
|
||||||
|
@ -23,6 +23,12 @@ object StudeezDestinations {
|
||||||
const val SESSION_SCREEN = "session"
|
const val SESSION_SCREEN = "session"
|
||||||
const val SESSION_RECAP = "session_recap"
|
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
|
// Friends flow
|
||||||
const val SEARCH_FRIENDS_SCREEN = "search_friends"
|
const val SEARCH_FRIENDS_SCREEN = "search_friends"
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,12 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.TextButton
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
|
@ -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 = {},
|
||||||
|
)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {} },
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package be.ugent.sel.studeez.screens.tasks.forms
|
||||||
|
|
||||||
|
data class SubjectFormUiState(
|
||||||
|
val name: String = "",
|
||||||
|
val color: Long = 0xFFFFD200,
|
||||||
|
)
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package be.ugent.sel.studeez.screens.tasks.forms
|
||||||
|
|
||||||
|
data class TaskFormUiState(
|
||||||
|
val name: String = "",
|
||||||
|
)
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,11 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import be.ugent.sel.studeez.R
|
import be.ugent.sel.studeez.R
|
||||||
|
|
|
@ -32,6 +32,14 @@
|
||||||
<!-- Tasks -->
|
<!-- Tasks -->
|
||||||
<string name="tasks">Tasks</string>
|
<string name="tasks">Tasks</string>
|
||||||
<string name="task">Task</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 -->
|
<!-- 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. -->
|
||||||
|
|
Reference in a new issue