commit
d9d83569db
26 changed files with 549 additions and 51 deletions
|
@ -3,10 +3,13 @@ package be.ugent.sel.studeez.common.composable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -23,4 +26,14 @@ fun Headline(
|
||||||
fontSize = 34.sp
|
fontSize = 34.sp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DateText(date: String) {
|
||||||
|
Text(
|
||||||
|
text = date,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
modifier = Modifier.padding(horizontal = 10.dp)
|
||||||
|
)
|
||||||
}
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
package be.ugent.sel.studeez.common.composable.feed
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.CircularProgressIndicator
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import be.ugent.sel.studeez.common.composable.BasicTextButton
|
||||||
|
import be.ugent.sel.studeez.common.composable.DateText
|
||||||
|
import be.ugent.sel.studeez.common.composable.Headline
|
||||||
|
import be.ugent.sel.studeez.common.ext.textButton
|
||||||
|
import be.ugent.sel.studeez.data.local.models.FeedEntry
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
|
||||||
|
import be.ugent.sel.studeez.R.string as AppText
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Feed(
|
||||||
|
uiState: FeedUiState,
|
||||||
|
continueTask: (String, String) -> Unit,
|
||||||
|
onEmptyFeedHelp: () -> Unit
|
||||||
|
) {
|
||||||
|
when (uiState) {
|
||||||
|
FeedUiState.Loading -> LoadingFeed()
|
||||||
|
is FeedUiState.Succes -> LoadedFeed(
|
||||||
|
uiState = uiState,
|
||||||
|
continueTask = continueTask,
|
||||||
|
onEmptyFeedHelp = onEmptyFeedHelp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoadedFeed(
|
||||||
|
uiState: FeedUiState.Succes,
|
||||||
|
continueTask: (String, String) -> Unit,
|
||||||
|
onEmptyFeedHelp: () -> Unit,
|
||||||
|
) {
|
||||||
|
if (uiState.feedEntries.isEmpty()) EmptyFeed(onEmptyFeedHelp)
|
||||||
|
else FeedWithElements(uiState = uiState, continueTask = continueTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoadingFeed() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(color = MaterialTheme.colors.onBackground)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FeedWithElements(
|
||||||
|
uiState: FeedUiState.Succes,
|
||||||
|
continueTask: (String, String) -> Unit,
|
||||||
|
) {
|
||||||
|
val feedEntries = uiState.feedEntries
|
||||||
|
LazyColumn {
|
||||||
|
items(feedEntries.toList()) { (date, feedEntries) ->
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(10.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
val totalDayStudyTime: Int = feedEntries.sumOf { it.totalStudyTime }
|
||||||
|
DateText(date = date)
|
||||||
|
Text(
|
||||||
|
text = "${HoursMinutesSeconds(totalDayStudyTime)}",
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
feedEntries.forEach { feedEntry ->
|
||||||
|
FeedEntry(feedEntry = feedEntry) {
|
||||||
|
continueTask(feedEntry.subjectId, feedEntry.taskId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EmptyFeed(onEmptyFeedHelp: () -> Unit) {
|
||||||
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Headline(text = stringResource(id = AppText.your_feed))
|
||||||
|
|
||||||
|
BasicTextButton(
|
||||||
|
AppText.empty_feed_help_text,
|
||||||
|
Modifier.textButton(),
|
||||||
|
action = onEmptyFeedHelp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun FeedLoadingPreview() {
|
||||||
|
Feed(
|
||||||
|
uiState = FeedUiState.Loading,
|
||||||
|
continueTask = { _, _ -> run {} }, {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun FeedPreview() {
|
||||||
|
Feed(
|
||||||
|
uiState = FeedUiState.Succes(
|
||||||
|
mapOf(
|
||||||
|
"08 May 2023" to listOf(
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFFFD200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
totalStudyTime = 600,
|
||||||
|
),
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFFFD200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
totalStudyTime = 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"09 May 2023" to listOf(
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFFD1200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
),
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFFFD200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
continueTask = { _, _ -> run {} }, {}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package be.ugent.sel.studeez.common.composable.feed
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.Card
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.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.FeedEntry
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FeedEntry(
|
||||||
|
feedEntry: FeedEntry,
|
||||||
|
continueWithTask: () -> 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(11f)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(20.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(Color(feedEntry.argb_color)),
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(0.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = feedEntry.subJectName,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = feedEntry.taskName,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StealthButton(
|
||||||
|
text = R.string.continue_task,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 10.dp, end = 5.dp)
|
||||||
|
.weight(6f)
|
||||||
|
) {
|
||||||
|
continueWithTask()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun FeedEntryPreview() {
|
||||||
|
FeedEntry(
|
||||||
|
continueWithTask = {},
|
||||||
|
feedEntry = FeedEntry(
|
||||||
|
argb_color = 0xFFFFD200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkk",
|
||||||
|
totalStudyTime = 20,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package be.ugent.sel.studeez.common.composable.feed
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.FeedEntry
|
||||||
|
|
||||||
|
sealed interface FeedUiState {
|
||||||
|
object Loading : FeedUiState
|
||||||
|
data class Succes(val feedEntries: Map<String, List<FeedEntry>>) : FeedUiState
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package be.ugent.sel.studeez.common.composable.feed
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import be.ugent.sel.studeez.data.SelectedTask
|
||||||
|
import be.ugent.sel.studeez.data.local.models.FeedEntry
|
||||||
|
import be.ugent.sel.studeez.domain.FeedDAO
|
||||||
|
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 kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class FeedViewModel @Inject constructor(
|
||||||
|
feedDAO: FeedDAO,
|
||||||
|
private val taskDAO: TaskDAO,
|
||||||
|
private val selectedTask: SelectedTask,
|
||||||
|
logService: LogService
|
||||||
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
|
val uiState: StateFlow<FeedUiState> = feedDAO.getFeedEntries()
|
||||||
|
.map { FeedUiState.Succes(it) }
|
||||||
|
.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
initialValue = FeedUiState.Loading,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun continueTask(open: (String) -> Unit, subjectId: String, taskId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val task = taskDAO.getTask(subjectId, taskId)
|
||||||
|
selectedTask.set(task)
|
||||||
|
open(StudeezDestinations.TIMER_SELECTION_SCREEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onEmptyFeedHelp(open: (String) -> Unit) {
|
||||||
|
open(StudeezDestinations.ADD_SUBJECT_FORM)
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ fun TaskEntry(
|
||||||
task: Task,
|
task: Task,
|
||||||
onCheckTask: (Boolean) -> Unit,
|
onCheckTask: (Boolean) -> Unit,
|
||||||
onDeleteTask: () -> Unit,
|
onDeleteTask: () -> Unit,
|
||||||
|
onStartTask: () -> Unit
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -95,6 +96,7 @@ fun TaskEntry(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = 5.dp),
|
.padding(end = 5.dp),
|
||||||
) {
|
) {
|
||||||
|
onStartTask()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +112,7 @@ fun TaskEntryPreview() {
|
||||||
name = "Test Task",
|
name = "Test Task",
|
||||||
completed = false,
|
completed = false,
|
||||||
),
|
),
|
||||||
{}, {},
|
{}, {}, {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +124,7 @@ fun CompletedTaskEntryPreview() {
|
||||||
name = "Test Task",
|
name = "Test Task",
|
||||||
completed = true,
|
completed = true,
|
||||||
),
|
),
|
||||||
{}, {},
|
{}, {}, {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +136,6 @@ fun OverflowTaskEntryPreview() {
|
||||||
name = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkkkk",
|
name = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkkkk",
|
||||||
completed = false,
|
completed = false,
|
||||||
),
|
),
|
||||||
{}, {},
|
{}, {}, {}
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package be.ugent.sel.studeez.data.local.models
|
||||||
|
|
||||||
|
import com.google.firebase.Timestamp
|
||||||
|
|
||||||
|
data class FeedEntry(
|
||||||
|
val argb_color: Long = 0,
|
||||||
|
val subJectName: String = "",
|
||||||
|
val taskName: String = "",
|
||||||
|
val taskId: String = "", // Name of task is not unique
|
||||||
|
val subjectId: String = "",
|
||||||
|
val totalStudyTime: Int = 0,
|
||||||
|
val endTime: Timestamp = Timestamp(0, 0)
|
||||||
|
)
|
|
@ -6,5 +6,7 @@ import com.google.firebase.firestore.DocumentId
|
||||||
data class SessionReport(
|
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),
|
||||||
|
val taskId: String = "",
|
||||||
|
val subjectId: String = ""
|
||||||
)
|
)
|
|
@ -2,6 +2,7 @@ package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||||
|
|
||||||
import be.ugent.sel.studeez.data.local.models.SessionReport
|
import be.ugent.sel.studeez.data.local.models.SessionReport
|
||||||
import com.google.firebase.Timestamp
|
import com.google.firebase.Timestamp
|
||||||
|
import com.google.firebase.firestore.DocumentReference
|
||||||
|
|
||||||
abstract class FunctionalTimer(initialValue: Int) {
|
abstract class FunctionalTimer(initialValue: Int) {
|
||||||
var time: Time = Time(initialValue)
|
var time: Time = Time(initialValue)
|
||||||
|
@ -17,10 +18,12 @@ abstract class FunctionalTimer(initialValue: Int) {
|
||||||
|
|
||||||
abstract fun hasCurrentCountdownEnded(): Boolean
|
abstract fun hasCurrentCountdownEnded(): Boolean
|
||||||
|
|
||||||
fun getSessionReport(): SessionReport {
|
fun getSessionReport(subjectId: String, taskId: String): SessionReport {
|
||||||
return SessionReport(
|
return SessionReport(
|
||||||
studyTime = totalStudyTime,
|
studyTime = totalStudyTime,
|
||||||
endTime = Timestamp.now()
|
endTime = Timestamp.now(),
|
||||||
|
taskId = taskId,
|
||||||
|
subjectId = subjectId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,4 +33,7 @@ abstract class DatabaseModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO
|
abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun provideFeedDAO(impl: FirebaseFeedDAO): FeedDAO
|
||||||
}
|
}
|
10
app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt
Normal file
10
app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package be.ugent.sel.studeez.domain
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.local.models.FeedEntry
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface FeedDAO {
|
||||||
|
|
||||||
|
fun getFeedEntries(): Flow<Map<String, List<FeedEntry>>>
|
||||||
|
|
||||||
|
}
|
|
@ -12,4 +12,5 @@ interface SubjectDAO {
|
||||||
fun deleteSubject(oldSubject: Subject)
|
fun deleteSubject(oldSubject: Subject)
|
||||||
|
|
||||||
fun updateSubject(newSubject: Subject)
|
fun updateSubject(newSubject: Subject)
|
||||||
|
suspend fun getSubject(subjectId: String): Subject?
|
||||||
}
|
}
|
|
@ -15,4 +15,6 @@ interface TaskDAO {
|
||||||
fun deleteTask(oldTask: Task)
|
fun deleteTask(oldTask: Task)
|
||||||
|
|
||||||
fun toggleTaskCompleted(task: Task, completed: Boolean)
|
fun toggleTaskCompleted(task: Task, completed: Boolean)
|
||||||
|
|
||||||
|
suspend fun getTask(subjectId: String, taskId: String): Task
|
||||||
}
|
}
|
|
@ -1,13 +1,16 @@
|
||||||
package be.ugent.sel.studeez.domain.implementation
|
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.Subject
|
||||||
|
import be.ugent.sel.studeez.data.local.models.task.Task
|
||||||
import be.ugent.sel.studeez.domain.AccountDAO
|
import be.ugent.sel.studeez.domain.AccountDAO
|
||||||
import be.ugent.sel.studeez.domain.SubjectDAO
|
import be.ugent.sel.studeez.domain.SubjectDAO
|
||||||
import com.google.firebase.firestore.CollectionReference
|
import com.google.firebase.firestore.CollectionReference
|
||||||
import com.google.firebase.firestore.FirebaseFirestore
|
import com.google.firebase.firestore.FirebaseFirestore
|
||||||
import com.google.firebase.firestore.ktx.snapshots
|
import com.google.firebase.firestore.ktx.snapshots
|
||||||
|
import com.google.firebase.firestore.ktx.toObject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.tasks.await
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class FireBaseSubjectDAO @Inject constructor(
|
class FireBaseSubjectDAO @Inject constructor(
|
||||||
|
@ -20,6 +23,10 @@ class FireBaseSubjectDAO @Inject constructor(
|
||||||
.map { it.toObjects(Subject::class.java) }
|
.map { it.toObjects(Subject::class.java) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getSubject(subjectId: String): Subject? {
|
||||||
|
return currentUserSubjectsCollection().document(subjectId).get().await().toObject()
|
||||||
|
}
|
||||||
|
|
||||||
override fun saveSubject(newSubject: Subject) {
|
override fun saveSubject(newSubject: Subject) {
|
||||||
currentUserSubjectsCollection().add(newSubject)
|
currentUserSubjectsCollection().add(newSubject)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,11 @@ import be.ugent.sel.studeez.domain.TaskDAO
|
||||||
import com.google.firebase.firestore.CollectionReference
|
import com.google.firebase.firestore.CollectionReference
|
||||||
import com.google.firebase.firestore.FirebaseFirestore
|
import com.google.firebase.firestore.FirebaseFirestore
|
||||||
import com.google.firebase.firestore.ktx.snapshots
|
import com.google.firebase.firestore.ktx.snapshots
|
||||||
|
import com.google.firebase.firestore.ktx.toObject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.tasks.await
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class FireBaseTaskDAO @Inject constructor(
|
class FireBaseTaskDAO @Inject constructor(
|
||||||
|
@ -22,6 +25,10 @@ class FireBaseTaskDAO @Inject constructor(
|
||||||
.map { it.toObjects(Task::class.java) }
|
.map { it.toObjects(Task::class.java) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getTask(subjectId: String, taskId: String): Task {
|
||||||
|
return selectedSubjectTasksCollection(subjectId).document(taskId).get().await().toObject()!!
|
||||||
|
}
|
||||||
|
|
||||||
override fun saveTask(newTask: Task) {
|
override fun saveTask(newTask: Task) {
|
||||||
selectedSubjectTasksCollection(newTask.subjectId).add(newTask)
|
selectedSubjectTasksCollection(newTask.subjectId).add(newTask)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package be.ugent.sel.studeez.domain.implementation
|
||||||
|
|
||||||
|
import android.icu.text.DateFormat
|
||||||
|
import be.ugent.sel.studeez.data.local.models.FeedEntry
|
||||||
|
import be.ugent.sel.studeez.data.local.models.SessionReport
|
||||||
|
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.FeedDAO
|
||||||
|
import be.ugent.sel.studeez.domain.SessionDAO
|
||||||
|
import be.ugent.sel.studeez.domain.TaskDAO
|
||||||
|
import com.google.firebase.Timestamp
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class FirebaseFeedDAO @Inject constructor(
|
||||||
|
private val sessionDAO: SessionDAO,
|
||||||
|
private val taskDAO: TaskDAO,
|
||||||
|
private val subjectDAO: FireBaseSubjectDAO
|
||||||
|
) : FeedDAO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a map as with key the day and value a list of feedentries for that day.
|
||||||
|
*/
|
||||||
|
override fun getFeedEntries(): Flow<Map<String, List<FeedEntry>>> {
|
||||||
|
return sessionDAO.getSessions().map {sessionReports ->
|
||||||
|
sessionReports
|
||||||
|
.map { sessionReport -> sessionToFeedEntry(sessionReport) }
|
||||||
|
.sortedByDescending { it.endTime }
|
||||||
|
.groupBy { getFormattedTime(it) }
|
||||||
|
.mapValues { (_, entries) ->
|
||||||
|
entries
|
||||||
|
.groupBy { it.taskId }
|
||||||
|
.map { fuseFeedEntries(it.component2()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFormattedTime(entry: FeedEntry): String {
|
||||||
|
return DateFormat.getDateInstance().format(entry.endTime.toDate())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Givin a list of entries referencing the same task, in the same day, fuse them into one
|
||||||
|
* feed-entry by adding the studytime and keeping the most recent end-timestamp
|
||||||
|
*/
|
||||||
|
private fun fuseFeedEntries(entries: List<FeedEntry>): FeedEntry =
|
||||||
|
entries.drop(1).fold(entries[0]) { accEntry, newEntry ->
|
||||||
|
accEntry.copy(
|
||||||
|
totalStudyTime = accEntry.totalStudyTime + newEntry.totalStudyTime,
|
||||||
|
endTime = getMostRecent(accEntry.endTime, newEntry.endTime)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMostRecent(t1: Timestamp, t2: Timestamp): Timestamp {
|
||||||
|
return if (t1 < t2) t2 else t1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a sessionReport to a feedEntry. Fetch Task and Subject to get names
|
||||||
|
*/
|
||||||
|
private suspend fun sessionToFeedEntry(sessionReport: SessionReport): FeedEntry {
|
||||||
|
val subjectId: String = sessionReport.subjectId
|
||||||
|
val taskId: String = sessionReport.taskId
|
||||||
|
|
||||||
|
val task: Task = taskDAO.getTask(subjectId, taskId)
|
||||||
|
val subject: Subject = subjectDAO.getSubject(subjectId)!!
|
||||||
|
|
||||||
|
return FeedEntry(
|
||||||
|
argb_color = subject.argb_color,
|
||||||
|
subJectName = subject.name,
|
||||||
|
taskName = task.name,
|
||||||
|
taskId = task.id,
|
||||||
|
subjectId = subject.id,
|
||||||
|
totalStudyTime = sessionReport.studyTime,
|
||||||
|
endTime = sessionReport.endTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,9 +65,9 @@ fun StudeezNavGraph(
|
||||||
composable(StudeezDestinations.HOME_SCREEN) {
|
composable(StudeezDestinations.HOME_SCREEN) {
|
||||||
HomeRoute(
|
HomeRoute(
|
||||||
open,
|
open,
|
||||||
viewModel = hiltViewModel(),
|
|
||||||
drawerActions = drawerActions,
|
drawerActions = drawerActions,
|
||||||
navigationBarActions = navigationBarActions
|
navigationBarActions = navigationBarActions,
|
||||||
|
feedViewModel = hiltViewModel(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,35 +5,45 @@ import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import be.ugent.sel.studeez.R
|
import be.ugent.sel.studeez.R
|
||||||
import be.ugent.sel.studeez.common.composable.BasicButton
|
|
||||||
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
|
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
|
||||||
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
|
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
|
||||||
|
import be.ugent.sel.studeez.common.composable.feed.Feed
|
||||||
|
import be.ugent.sel.studeez.common.composable.feed.FeedUiState
|
||||||
|
import be.ugent.sel.studeez.common.composable.feed.FeedViewModel
|
||||||
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
|
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
|
||||||
import be.ugent.sel.studeez.common.ext.basicButton
|
import be.ugent.sel.studeez.data.local.models.FeedEntry
|
||||||
import be.ugent.sel.studeez.resources
|
import be.ugent.sel.studeez.resources
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeRoute(
|
fun HomeRoute(
|
||||||
open: (String) -> Unit,
|
open: (String) -> Unit,
|
||||||
viewModel: HomeViewModel,
|
|
||||||
drawerActions: DrawerActions,
|
drawerActions: DrawerActions,
|
||||||
navigationBarActions: NavigationBarActions,
|
navigationBarActions: NavigationBarActions,
|
||||||
|
feedViewModel: FeedViewModel,
|
||||||
) {
|
) {
|
||||||
|
val feedUiState by feedViewModel.uiState.collectAsState()
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
onStartSessionClick = { viewModel.onStartSessionClick(open) },
|
|
||||||
drawerActions = drawerActions,
|
drawerActions = drawerActions,
|
||||||
|
open = open,
|
||||||
navigationBarActions = navigationBarActions,
|
navigationBarActions = navigationBarActions,
|
||||||
|
feedUiState = feedUiState,
|
||||||
|
continueTask = { subjectId, taskId -> feedViewModel.continueTask(open, subjectId, taskId) },
|
||||||
|
onEmptyFeedHelp = { feedViewModel.onEmptyFeedHelp(open) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
onStartSessionClick: () -> Unit,
|
open: (String) -> Unit,
|
||||||
drawerActions: DrawerActions,
|
drawerActions: DrawerActions,
|
||||||
navigationBarActions: NavigationBarActions
|
navigationBarActions: NavigationBarActions,
|
||||||
|
feedUiState: FeedUiState,
|
||||||
|
continueTask: (String, String) -> Unit,
|
||||||
|
onEmptyFeedHelp: () -> Unit,
|
||||||
) {
|
) {
|
||||||
PrimaryScreenTemplate(
|
PrimaryScreenTemplate(
|
||||||
title = resources().getString(R.string.home),
|
title = resources().getString(R.string.home),
|
||||||
|
@ -41,9 +51,7 @@ fun HomeScreen(
|
||||||
navigationBarActions = navigationBarActions,
|
navigationBarActions = navigationBarActions,
|
||||||
// TODO barAction = { FriendsAction() }
|
// TODO barAction = { FriendsAction() }
|
||||||
) {
|
) {
|
||||||
BasicButton(R.string.start_session, Modifier.basicButton()) {
|
Feed(feedUiState, continueTask, onEmptyFeedHelp)
|
||||||
onStartSessionClick()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +69,40 @@ fun FriendsAction() {
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreenPreview() {
|
fun HomeScreenPreview() {
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
onStartSessionClick = {},
|
|
||||||
drawerActions = DrawerActions({}, {}, {}, {}, {}),
|
drawerActions = DrawerActions({}, {}, {}, {}, {}),
|
||||||
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {})
|
navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}),
|
||||||
|
open = {},
|
||||||
|
feedUiState = FeedUiState.Succes(
|
||||||
|
mapOf(
|
||||||
|
"08 May 2023" to listOf(
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFABD200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
totalStudyTime = 600,
|
||||||
|
),
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFFFD200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
totalStudyTime = 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"09 May 2023" to listOf(
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFFD1200,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
),
|
||||||
|
FeedEntry(
|
||||||
|
argb_color = 0xFFFF5C89,
|
||||||
|
subJectName = "Test Subject",
|
||||||
|
taskName = "Test Task",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
continueTask = { _, _ -> run {} },
|
||||||
|
onEmptyFeedHelp = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
package be.ugent.sel.studeez.screens.home
|
|
||||||
|
|
||||||
import be.ugent.sel.studeez.domain.AccountDAO
|
|
||||||
import be.ugent.sel.studeez.domain.LogService
|
|
||||||
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 HomeViewModel @Inject constructor(
|
|
||||||
private val accountDAO: AccountDAO,
|
|
||||||
logService: LogService
|
|
||||||
) : StudeezViewModel(logService) {
|
|
||||||
|
|
||||||
fun onStartSessionClick(open: (String) -> Unit) {
|
|
||||||
open(StudeezDestinations.TIMER_SELECTION_SCREEN)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,9 @@
|
||||||
package be.ugent.sel.studeez.screens.session
|
package be.ugent.sel.studeez.screens.session
|
||||||
|
|
||||||
|
import be.ugent.sel.studeez.data.SelectedTask
|
||||||
import be.ugent.sel.studeez.data.SelectedTimerState
|
import be.ugent.sel.studeez.data.SelectedTimerState
|
||||||
import be.ugent.sel.studeez.data.SessionReportState
|
import be.ugent.sel.studeez.data.SessionReportState
|
||||||
|
import be.ugent.sel.studeez.data.local.models.task.Task
|
||||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||||
import be.ugent.sel.studeez.domain.LogService
|
import be.ugent.sel.studeez.domain.LogService
|
||||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
||||||
|
@ -13,21 +15,22 @@ import javax.inject.Inject
|
||||||
class SessionViewModel @Inject constructor(
|
class SessionViewModel @Inject constructor(
|
||||||
private val selectedTimerState: SelectedTimerState,
|
private val selectedTimerState: SelectedTimerState,
|
||||||
private val sessionReportState: SessionReportState,
|
private val sessionReportState: SessionReportState,
|
||||||
|
private val selectedTask: SelectedTask,
|
||||||
logService: LogService
|
logService: LogService
|
||||||
) : StudeezViewModel(logService) {
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
private val task : String = "No task selected" // placeholder for tasks implementation
|
private val task : Task = selectedTask()
|
||||||
|
|
||||||
fun getTimer() : FunctionalTimer {
|
fun getTimer() : FunctionalTimer {
|
||||||
return selectedTimerState.selectedTimer!!
|
return selectedTimerState.selectedTimer!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTask(): String {
|
fun getTask(): String {
|
||||||
return task
|
return task.name
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endSession(openAndPopUp: (String, String) -> Unit) {
|
fun endSession(openAndPopUp: (String, String) -> Unit) {
|
||||||
sessionReportState.sessionReport = getTimer().getSessionReport()
|
sessionReportState.sessionReport = getTimer().getSessionReport(task.subjectId, task.id)
|
||||||
openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN)
|
openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,6 +30,7 @@ data class TaskActions(
|
||||||
val deleteTask: (Task) -> Unit,
|
val deleteTask: (Task) -> Unit,
|
||||||
val onCheckTask: (Task, Boolean) -> Unit,
|
val onCheckTask: (Task, Boolean) -> Unit,
|
||||||
val editSubject: () -> Unit,
|
val editSubject: () -> Unit,
|
||||||
|
val startTask: (Task) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions {
|
fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions {
|
||||||
|
@ -39,7 +40,8 @@ fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskAction
|
||||||
getSubject = viewModel::getSelectedSubject,
|
getSubject = viewModel::getSelectedSubject,
|
||||||
deleteTask = viewModel::deleteTask,
|
deleteTask = viewModel::deleteTask,
|
||||||
onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) },
|
onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) },
|
||||||
editSubject = { viewModel.editSubject(open) }
|
editSubject = { viewModel.editSubject(open) },
|
||||||
|
startTask = { task -> viewModel.startTask(task, open) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +77,7 @@ fun TaskScreen(
|
||||||
task = it,
|
task = it,
|
||||||
onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) },
|
onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) },
|
||||||
onDeleteTask = { taskActions.deleteTask(it) },
|
onDeleteTask = { taskActions.deleteTask(it) },
|
||||||
|
onStartTask = { taskActions.startTask(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +111,7 @@ fun TaskScreenPreview() {
|
||||||
{},
|
{},
|
||||||
{ _, _ -> run {} },
|
{ _, _ -> run {} },
|
||||||
{},
|
{},
|
||||||
|
{}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package be.ugent.sel.studeez.screens.tasks
|
package be.ugent.sel.studeez.screens.tasks
|
||||||
|
|
||||||
import be.ugent.sel.studeez.data.SelectedSubject
|
import be.ugent.sel.studeez.data.SelectedSubject
|
||||||
|
import be.ugent.sel.studeez.data.SelectedTask
|
||||||
import be.ugent.sel.studeez.data.local.models.task.Subject
|
import be.ugent.sel.studeez.data.local.models.task.Subject
|
||||||
import be.ugent.sel.studeez.data.local.models.task.Task
|
import be.ugent.sel.studeez.data.local.models.task.Task
|
||||||
import be.ugent.sel.studeez.domain.LogService
|
import be.ugent.sel.studeez.domain.LogService
|
||||||
|
@ -17,6 +18,7 @@ class TaskViewModel @Inject constructor(
|
||||||
private val taskDAO: TaskDAO,
|
private val taskDAO: TaskDAO,
|
||||||
private val subjectDAO: SubjectDAO,
|
private val subjectDAO: SubjectDAO,
|
||||||
private val selectedSubject: SelectedSubject,
|
private val selectedSubject: SelectedSubject,
|
||||||
|
private val selectedTask: SelectedTask,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
) : StudeezViewModel(logService) {
|
) : StudeezViewModel(logService) {
|
||||||
fun addTask(open: (String) -> Unit) {
|
fun addTask(open: (String) -> Unit) {
|
||||||
|
@ -47,4 +49,9 @@ class TaskViewModel @Inject constructor(
|
||||||
fun editSubject(open: (String) -> Unit) {
|
fun editSubject(open: (String) -> Unit) {
|
||||||
open(StudeezDestinations.EDIT_SUBJECT_FORM)
|
open(StudeezDestinations.EDIT_SUBJECT_FORM)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startTask(task: Task, open: (String) -> Unit) {
|
||||||
|
selectedTask.set(task)
|
||||||
|
open(StudeezDestinations.TIMER_SELECTION_SCREEN)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -82,12 +82,13 @@ fun TimerOverviewScreen(
|
||||||
items(timers.value) { timerInfo ->
|
items(timers.value) { timerInfo ->
|
||||||
TimerEntry(
|
TimerEntry(
|
||||||
timerInfo = timerInfo,
|
timerInfo = timerInfo,
|
||||||
) {
|
rightButton = {
|
||||||
StealthButton(
|
StealthButton(
|
||||||
text = R.string.edit,
|
text = R.string.edit,
|
||||||
onClick = { timerOverviewActions.onEditClick(timerInfo) }
|
onClick = { timerOverviewActions.onEditClick(timerInfo) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package be.ugent.sel.studeez.screens.timer_selection
|
package be.ugent.sel.studeez.screens.timer_selection
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import be.ugent.sel.studeez.R
|
import be.ugent.sel.studeez.R
|
||||||
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
|
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
|
||||||
import be.ugent.sel.studeez.common.composable.StealthButton
|
import be.ugent.sel.studeez.common.composable.StealthButton
|
||||||
|
@ -99,7 +102,10 @@ fun CustomTimerEntry(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
rightButton = {
|
rightButton = {
|
||||||
TimePickerButton(initialSeconds = hms.getTotalSeconds()) { chosenTime ->
|
TimePickerButton(
|
||||||
|
initialSeconds = hms.getTotalSeconds(),
|
||||||
|
modifier = Modifier.padding(horizontal = 5.dp)
|
||||||
|
) { chosenTime ->
|
||||||
timerInfo.studyTime = chosenTime
|
timerInfo.studyTime = chosenTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import be.ugent.sel.studeez.data.SelectedTimerState
|
import be.ugent.sel.studeez.data.SelectedTimerState
|
||||||
|
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
|
||||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
|
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
|
||||||
import be.ugent.sel.studeez.domain.LogService
|
import be.ugent.sel.studeez.domain.LogService
|
||||||
import be.ugent.sel.studeez.domain.TimerDAO
|
import be.ugent.sel.studeez.domain.TimerDAO
|
||||||
|
@ -21,7 +22,9 @@ class TimerSelectionViewModel @Inject constructor(
|
||||||
logService: LogService
|
logService: LogService
|
||||||
) : StudeezViewModel(logService) {
|
) : StudeezViewModel(logService) {
|
||||||
|
|
||||||
var customTimerStudyTime: MutableState<Int> = mutableStateOf(0)
|
var customTimerStudyTime: MutableState<Int> = mutableStateOf(
|
||||||
|
HoursMinutesSeconds(1, 0, 0).getTotalSeconds()
|
||||||
|
)
|
||||||
|
|
||||||
fun getAllTimers() : Flow<List<TimerInfo>> {
|
fun getAllTimers() : Flow<List<TimerInfo>> {
|
||||||
return timerDAO.getAllTimers()
|
return timerDAO.getAllTimers()
|
||||||
|
|
|
@ -29,6 +29,11 @@
|
||||||
<string name="home">Home</string>
|
<string name="home">Home</string>
|
||||||
<string name="start_session">Start session</string>
|
<string name="start_session">Start session</string>
|
||||||
|
|
||||||
|
<!-- Feed-->
|
||||||
|
<string name="continue_task">Continue</string>
|
||||||
|
<string name="your_feed">This is your feed</string>
|
||||||
|
<string name="empty_feed_help_text">Create you first subject and tasks to get started</string>
|
||||||
|
|
||||||
<!-- Tasks -->
|
<!-- Tasks -->
|
||||||
<string name="tasks">Tasks</string>
|
<string name="tasks">Tasks</string>
|
||||||
<string name="task">Task</string>
|
<string name="task">Task</string>
|
||||||
|
|
Reference in a new issue