From 3fb8b08cb4b89248df6b7066f6d03dea9abf6084 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Tue, 2 May 2023 00:18:36 +0200 Subject: [PATCH 001/163] added midsection --- .../session/sessionScreens/AbstractSessionScreen.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt index 06a80b6..3937ab9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt @@ -95,6 +95,8 @@ abstract class AbstractSessionScreen { fontSize = 30.sp ) + MidSection() + Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() @@ -121,6 +123,11 @@ abstract class AbstractSessionScreen { @Composable abstract fun motivationString(): String + @Composable + open fun MidSection() { + // Default has no midsection, unless overwritten. + } + abstract fun callMediaPlayer() } From 47620f69ed3a8ab18d900976336f4b0e80c1d17f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Tue, 2 May 2023 00:18:59 +0200 Subject: [PATCH 002/163] added midsection implementation --- .../sessionScreens/BreakSessionScreen.kt | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt index 8fa45ff..1caeacc 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt @@ -1,7 +1,20 @@ package be.ugent.sel.studeez.screens.session.sessionScreens import android.media.MediaPlayer +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import be.ugent.sel.studeez.R import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.resources @@ -12,6 +25,37 @@ class BreakSessionScreen( private var mediaplayer: MediaPlayer? ): AbstractSessionScreen() { + @Composable + override fun MidSection() { + Dots() + } + + @Composable + fun Dots() { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + repeat(funPomoDoroTimer.breaksRemaining - 1) { + Dot(color = Color.DarkGray) + } + if (funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) + repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) { + Dot(color = Color.Gray) + } + } + } + + @Composable + private fun Dot(color: Color) { + Box(modifier = Modifier + .padding(5.dp) + .size(10.dp) + .clip(CircleShape) + .background(color)) + } + @Composable override fun motivationString(): String { if (funPomoDoroTimer.isInBreak) { @@ -22,11 +66,7 @@ class BreakSessionScreen( return resources().getString(AppText.state_done) } - return resources().getQuantityString( - R.plurals.state_focus_remaining, - funPomoDoroTimer.breaksRemaining, - funPomoDoroTimer.breaksRemaining - ) + return resources().getString(AppText.state_focus) } override fun callMediaPlayer() { @@ -42,4 +82,12 @@ class BreakSessionScreen( mediaplayer?.start() } } +} + +@Preview +@Composable +fun MidsectionPreview() { + val funPomoDoroTimer = FunctionalPomodoroTimer(15, 60, 5) + val breakSessionScreen = BreakSessionScreen(funPomoDoroTimer, MediaPlayer()) + breakSessionScreen.MidSection() } \ No newline at end of file From 173eeb10736dffaae0066e8cb02d45ab5df970ab Mon Sep 17 00:00:00 2001 From: lbarraga Date: Tue, 2 May 2023 00:19:52 +0200 Subject: [PATCH 003/163] make repeats public --- .../local/models/timer_functional/FunctionalPomodoroTimer.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt index 8eeb1c6..9771bde 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt @@ -2,7 +2,8 @@ package be.ugent.sel.studeez.data.local.models.timer_functional class FunctionalPomodoroTimer( private var studyTime: Int, - private var breakTime: Int, repeats: Int + private var breakTime: Int, + val repeats: Int ) : FunctionalTimer(studyTime) { var breaksRemaining = repeats From 6fb597c7e169020d0dc904c5c2d727c289c50d30 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 2 May 2023 16:17:12 +0200 Subject: [PATCH 004/163] dots in omgekeerde volgorde --- .../screens/session/sessionScreens/BreakSessionScreen.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt index 1caeacc..9c59b46 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt @@ -37,11 +37,11 @@ class BreakSessionScreen( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { - repeat(funPomoDoroTimer.breaksRemaining - 1) { + repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) { Dot(color = Color.DarkGray) } - if (funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) - repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) { + if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) + repeat(funPomoDoroTimer.breaksRemaining - 1) { Dot(color = Color.Gray) } } From 47edf650ed74f2fb63679f17a01aa464df150b53 Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Fri, 5 May 2023 10:48:34 +0200 Subject: [PATCH 005/163] testen gefixt --- .../studeez/timer_functional/TimeUnitTest.kt | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/src/test/java/be/ugent/sel/studeez/timer_functional/TimeUnitTest.kt b/app/src/test/java/be/ugent/sel/studeez/timer_functional/TimeUnitTest.kt index 86fdce8..0d9bf4b 100644 --- a/app/src/test/java/be/ugent/sel/studeez/timer_functional/TimeUnitTest.kt +++ b/app/src/test/java/be/ugent/sel/studeez/timer_functional/TimeUnitTest.kt @@ -10,7 +10,7 @@ class TimeUnitTest { private val hours = 4 private val minutes = 20 private val seconds = 39 - private val time: Time = Time(seconds + minutes * 60 + hours * 60 * 60) + private var time: Time = Time(seconds + minutes * 60 + hours * 60 * 60) @Before fun setup() { @@ -21,9 +21,9 @@ class TimeUnitTest { fun formatTime() { Assert.assertEquals( HoursMinutesSeconds( - hours.toString().padStart(2, '0'), - minutes.toString().padStart(2, '0'), - seconds.toString().padStart(2, '0'), + hours, + minutes, + seconds, ), time.getAsHMS(), ) @@ -39,7 +39,11 @@ class TimeUnitTest { @Test fun minOne() { - time.minOne() + Assert.assertEquals( + (seconds + minutes * 60 + hours * 60 * 60), + time.time, + ) + time-- Assert.assertEquals( (seconds + minutes * 60 + hours * 60 * 60) - 1, time.time, @@ -48,7 +52,7 @@ class TimeUnitTest { @Test fun plusOne() { - time.plusOne() + time++ Assert.assertEquals( (seconds + minutes * 60 + hours * 60 * 60) + 1, time.time, @@ -59,7 +63,7 @@ class TimeUnitTest { fun minMultiple() { val n = 10 for (i in 1 .. n) { - time.minOne() + time-- } Assert.assertEquals( (seconds + minutes * 60 + hours * 60 * 60) - n, @@ -71,7 +75,7 @@ class TimeUnitTest { fun plusMultiple() { val n = 10 for (i in 1 .. n) { - time.plusOne() + time++ } Assert.assertEquals( (seconds + minutes * 60 + hours * 60 * 60) + n, From 1eff33a6db0c5d101f9d54d3046cf14e4e5bfb3e Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 09:37:07 +0200 Subject: [PATCH 006/163] start task now goes to timer selection screen --- .../sel/studeez/common/composable/tasks/TaskEntry.kt | 8 +++++--- .../ugent/sel/studeez/screens/session/SessionViewModel.kt | 7 +++++-- .../java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt | 6 +++++- .../be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt | 7 +++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt index fefb924..bf3a7cf 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt @@ -32,6 +32,7 @@ fun TaskEntry( task: Task, onCheckTask: (Boolean) -> Unit, onDeleteTask: () -> Unit, + onStartTask: () -> Unit ) { Card( modifier = Modifier @@ -95,6 +96,7 @@ fun TaskEntry( modifier = Modifier .padding(end = 5.dp), ) { + onStartTask() } } } @@ -110,7 +112,7 @@ fun TaskEntryPreview() { name = "Test Task", completed = false, ), - {}, {}, + {}, {}, {} ) } @@ -122,7 +124,7 @@ fun CompletedTaskEntryPreview() { name = "Test Task", completed = true, ), - {}, {}, + {}, {}, {}, ) } @@ -134,6 +136,6 @@ fun OverflowTaskEntryPreview() { name = "Test Taskkkkkkkkkkkkkkkkkkkkkkkkkkk", completed = false, ), - {}, {}, + {}, {}, {} ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt index d5e2bab..4b486fc 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt @@ -1,7 +1,9 @@ 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.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.domain.LogService import be.ugent.sel.studeez.navigation.StudeezDestinations @@ -13,17 +15,18 @@ import javax.inject.Inject class SessionViewModel @Inject constructor( private val selectedTimerState: SelectedTimerState, private val sessionReportState: SessionReportState, + private val selectedTask: SelectedTask, logService: LogService ) : StudeezViewModel(logService) { - private val task : String = "No task selected" // placeholder for tasks implementation + private val task : Task = selectedTask() fun getTimer() : FunctionalTimer { return selectedTimerState.selectedTimer!! } fun getTask(): String { - return task + return task.name } fun endSession(openAndPopUp: (String, String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt index 67f0e93..8a35717 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt @@ -30,6 +30,7 @@ data class TaskActions( val deleteTask: (Task) -> Unit, val onCheckTask: (Task, Boolean) -> Unit, val editSubject: () -> Unit, + val startTask: (Task) -> Unit ) fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions { @@ -39,7 +40,8 @@ fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskAction getSubject = viewModel::getSelectedSubject, deleteTask = viewModel::deleteTask, 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, onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, onDeleteTask = { taskActions.deleteTask(it) }, + onStartTask = { taskActions.startTask(it) } ) } } @@ -108,6 +111,7 @@ fun TaskScreenPreview() { {}, { _, _ -> run {} }, {}, + {} ) ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt index 138d32c..37f1c91 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt @@ -1,6 +1,7 @@ package be.ugent.sel.studeez.screens.tasks 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.Task import be.ugent.sel.studeez.domain.LogService @@ -17,6 +18,7 @@ class TaskViewModel @Inject constructor( private val taskDAO: TaskDAO, private val subjectDAO: SubjectDAO, private val selectedSubject: SelectedSubject, + private val selectedTask: SelectedTask, logService: LogService, ) : StudeezViewModel(logService) { fun addTask(open: (String) -> Unit) { @@ -47,4 +49,9 @@ class TaskViewModel @Inject constructor( fun editSubject(open: (String) -> Unit) { open(StudeezDestinations.EDIT_SUBJECT_FORM) } + + fun startTask(task: Task, open: (String) -> Unit) { + selectedTask.set(task) + open(StudeezDestinations.TIMER_SELECTION_SCREEN) + } } \ No newline at end of file From 45f28592abb43d9077572b73c3feaba19985b8c5 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 10:08:02 +0200 Subject: [PATCH 007/163] sessionreport now has the id of the task --- .../be/ugent/sel/studeez/data/local/models/SessionReport.kt | 3 ++- .../data/local/models/timer_functional/FunctionalTimer.kt | 6 ++++-- .../ugent/sel/studeez/screens/session/SessionViewModel.kt | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt index 20a44a8..9ed8450 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt @@ -6,5 +6,6 @@ import com.google.firebase.firestore.DocumentId data class SessionReport( @DocumentId val id: String = "", val studyTime: Int = 0, - val endTime: Timestamp = Timestamp(0, 0) + val endTime: Timestamp = Timestamp(0, 0), + val taskId: String ) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt index 1f4231a..cf5005f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt @@ -2,6 +2,7 @@ package be.ugent.sel.studeez.data.local.models.timer_functional import be.ugent.sel.studeez.data.local.models.SessionReport import com.google.firebase.Timestamp +import com.google.firebase.firestore.DocumentReference abstract class FunctionalTimer(initialValue: Int) { var time: Time = Time(initialValue) @@ -17,10 +18,11 @@ abstract class FunctionalTimer(initialValue: Int) { abstract fun hasCurrentCountdownEnded(): Boolean - fun getSessionReport(): SessionReport { + fun getSessionReport(taskId: String): SessionReport { return SessionReport( studyTime = totalStudyTime, - endTime = Timestamp.now() + endTime = Timestamp.now(), + taskId = taskId ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt index 4b486fc..920d4e0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt @@ -30,7 +30,7 @@ class SessionViewModel @Inject constructor( } fun endSession(openAndPopUp: (String, String) -> Unit) { - sessionReportState.sessionReport = getTimer().getSessionReport() + sessionReportState.sessionReport = getTimer().getSessionReport(task.id) openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN) } } \ No newline at end of file From 83fba0de70a520c7e0d7b25944b256f48aa73c25 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 13:55:44 +0200 Subject: [PATCH 008/163] #74 added a feedEntry dataclass --- .../ugent/sel/studeez/data/local/models/FeedEntry.kt | 10 ++++++++++ .../main/java/be/ugent/sel/studeez/domain/FeedDAO.kt | 4 ++++ .../studeez/domain/implementation/FirebaseFeedDAO.kt | 4 ++++ .../java/be/ugent/sel/studeez/screens/home/Feed.kt | 2 ++ .../be/ugent/sel/studeez/screens/home/FeedViewModel.kt | 4 ++++ 5 files changed, 24 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt new file mode 100644 index 0000000..336a252 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt @@ -0,0 +1,10 @@ +package be.ugent.sel.studeez.data.local.models + +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 +) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt new file mode 100644 index 0000000..cc8ae0c --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.domain + +interface FeedDAO { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt new file mode 100644 index 0000000..df9ef8f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.domain.implementation + +class FirebaseFeedDAO { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt new file mode 100644 index 0000000..af6014f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt @@ -0,0 +1,2 @@ +package be.ugent.sel.studeez.screens.home + diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt new file mode 100644 index 0000000..52c0be2 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.screens.home + +class FeedViewModel { +} \ No newline at end of file From 05d93246a68be8db05644e62c4cda560357068ea Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 13:56:08 +0200 Subject: [PATCH 009/163] sessionreport now also has a task and subject id --- .../be/ugent/sel/studeez/data/local/models/SessionReport.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt index 9ed8450..5835538 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/SessionReport.kt @@ -7,5 +7,6 @@ data class SessionReport( @DocumentId val id: String = "", val studyTime: Int = 0, val endTime: Timestamp = Timestamp(0, 0), - val taskId: String + val taskId: String = "", + val subjectId: String = "" ) \ No newline at end of file From a0ea97dc522b325237a768ceaf35516014a616dd Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 13:57:05 +0200 Subject: [PATCH 010/163] #74 added bind for feedDAO --- app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index 7ee4992..4c5fea1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -33,4 +33,7 @@ abstract class DatabaseModule { @Binds abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO + + @Binds + abstract fun provideFeedDAO(impl: FirebaseFeedDAO): FeedDAO } \ No newline at end of file From bee7101befefbd2a8f70059a7088021152b73186 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 13:58:02 +0200 Subject: [PATCH 011/163] #74 added FeedDAO and firebase implementation --- .../be/ugent/sel/studeez/domain/FeedDAO.kt | 6 +++ .../domain/implementation/FirebaseFeedDAO.kt | 49 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt index cc8ae0c..05d4425 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt @@ -1,4 +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> + } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index df9ef8f..c406b45 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -1,4 +1,51 @@ package be.ugent.sel.studeez.domain.implementation -class FirebaseFeedDAO { +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 kotlinx.coroutines.flow.* +import javax.inject.Inject + +class FirebaseFeedDAO @Inject constructor( + private val sessionDAO: SessionDAO, + private val taskDAO: TaskDAO, + private val subjectDAO: FireBaseSubjectDAO +) : FeedDAO { + + override fun getFeedEntries(): Flow> { + return sessionDAO.getSessions().map {sessionReports -> + sessionReports + .map { sessionReport -> sessionToFeedEntry(sessionReport) } + .groupBy { it.taskId } + .map { fuseFeedEntries(it.component2()) } + } + } + + private fun fuseFeedEntries(entries: List): FeedEntry = + entries.fold(entries[0]) { accEntry, newEntry -> + val newStudyTime = accEntry.totalStudyTime + newEntry.totalStudyTime + accEntry.copy(totalStudyTime = newStudyTime) + } + + + 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 + ) + } } \ No newline at end of file From dab4506a3451b55bcb1a012dd0bdfa7a46e3b512 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 13:59:29 +0200 Subject: [PATCH 012/163] #74 added single getSubject so feed kan fetch only one subject --- .../studeez/domain/implementation/FireBaseSubjectDAO.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt index 7d90fbf..e022863 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt @@ -1,13 +1,16 @@ 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.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 com.google.firebase.firestore.ktx.toObject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.tasks.await import javax.inject.Inject class FireBaseSubjectDAO @Inject constructor( @@ -20,6 +23,10 @@ class FireBaseSubjectDAO @Inject constructor( .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) { currentUserSubjectsCollection().add(newSubject) } From 2c7c96d73a1f4632ccdfb1feda717c881fa984dd Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 13:59:44 +0200 Subject: [PATCH 013/163] #74 added single getTask so feed kan fetch only one Task --- .../sel/studeez/domain/implementation/FireBaseTaskDAO.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt index b8855e6..963c93b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt @@ -8,8 +8,11 @@ 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 com.google.firebase.firestore.ktx.toObject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.tasks.await import javax.inject.Inject class FireBaseTaskDAO @Inject constructor( @@ -22,6 +25,10 @@ class FireBaseTaskDAO @Inject constructor( .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) { selectedSubjectTasksCollection(newTask.subjectId).add(newTask) } From 1c224446a847f6c2e6f09f8e05956e84ad26a92f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:00:42 +0200 Subject: [PATCH 014/163] added getSubject to interface --- app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt index 2749fac..da6abbe 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt @@ -12,4 +12,5 @@ interface SubjectDAO { fun deleteSubject(oldSubject: Subject) fun updateSubject(newSubject: Subject) + suspend fun getSubject(subjectId: String): Subject? } \ No newline at end of file From 07ff75476d0a309992c7b533dff1da03c64540c2 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:00:52 +0200 Subject: [PATCH 015/163] added getTask to interface --- app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt index 0f629ea..988a10d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt @@ -15,4 +15,6 @@ interface TaskDAO { fun deleteTask(oldTask: Task) fun toggleTaskCompleted(task: Task, completed: Boolean) + + suspend fun getTask(subjectId: String, taskId: String): Task } \ No newline at end of file From 2db431463db0cbb0372199e721e2431bb8e7c3bf Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:02:29 +0200 Subject: [PATCH 016/163] sessionreport needs if of subjectId and taskId --- .../data/local/models/timer_functional/FunctionalTimer.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt index cf5005f..656f52f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt @@ -18,11 +18,12 @@ abstract class FunctionalTimer(initialValue: Int) { abstract fun hasCurrentCountdownEnded(): Boolean - fun getSessionReport(taskId: String): SessionReport { + fun getSessionReport(subjectId: String, taskId: String): SessionReport { return SessionReport( studyTime = totalStudyTime, endTime = Timestamp.now(), - taskId = taskId + taskId = taskId, + subjectId = subjectId ) } From f7b5d5170d73412b4ebe22c3d312e3fb1f1334c7 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:02:56 +0200 Subject: [PATCH 017/163] #74 made a feed --- .../be/ugent/sel/studeez/screens/home/Feed.kt | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt index af6014f..0e93a96 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt @@ -1,2 +1,103 @@ package be.ugent.sel.studeez.screens.home +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Card +import androidx.compose.material.Divider +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.runtime.collectAsState +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.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +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.task.Subject +import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds + +@Composable +fun Feed( + open: (String) -> Unit, + viewModel: FeedViewModel = hiltViewModel() +) { + val feedEntries = viewModel.getFeedEntries().collectAsState(initial = emptyList()) + + LazyColumn { + items(feedEntries.value) {feedEntry -> + FeedEntryCard(feedEntry = feedEntry) { + viewModel.continueWithTask(open, feedEntry.subjectId, feedEntry.taskId) + } + } + } +} + +@Composable +fun FeedEntryCard( + feedEntry: FeedEntry, + 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(feedEntry.argb_color)), + ) + Column( + verticalArrangement = Arrangement.spacedBy(0.dp) + ) { + Text( + text = feedEntry.subJectName, + fontWeight = FontWeight.Bold, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text(text = feedEntry.taskName) + Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) + } + } + } + StealthButton( + text = R.string.start, + modifier = Modifier + .padding(start = 10.dp, end = 5.dp) + .weight(1f) + ) { + onViewSubject() + } + } + } +} \ No newline at end of file From 190c3467ec78aaf30f535bf4554c2a4f525d8f18 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:03:46 +0200 Subject: [PATCH 018/163] #74 viewmodel of a feed --- .../sel/studeez/screens/home/FeedViewModel.kt | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt index 52c0be2..3b9fb0c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt @@ -1,4 +1,37 @@ package be.ugent.sel.studeez.screens.home -class FeedViewModel { +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.Flow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class FeedViewModel @Inject constructor( + private val feedDAO: FeedDAO, + private val taskDAO: TaskDAO, + private val selectedTask: SelectedTask, + logService: LogService +) : StudeezViewModel(logService) { + + private val entries: Flow> = feedDAO.getFeedEntries() + + fun getFeedEntries(): Flow> { + return entries + } + + fun continueWithTask(open: (String) -> Unit, subjectId: String, taskId: String) { + viewModelScope.launch { + val task = taskDAO.getTask(subjectId, taskId) + selectedTask.set(task) + open(StudeezDestinations.TIMER_SELECTION_SCREEN) + } + } } \ No newline at end of file From 4728755c5e863b2132a27fce153c4258ffed74b7 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:04:20 +0200 Subject: [PATCH 019/163] #74 homescreen now contains a feed --- .../be/ugent/sel/studeez/screens/home/HomeScreen.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index f02852e..b71ada1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -25,6 +25,7 @@ fun HomeRoute( HomeScreen( onStartSessionClick = { viewModel.onStartSessionClick(open) }, drawerActions = drawerActions, + open = open, navigationBarActions = navigationBarActions, ) } @@ -32,6 +33,7 @@ fun HomeRoute( @Composable fun HomeScreen( onStartSessionClick: () -> Unit, + open: (String) -> Unit, drawerActions: DrawerActions, navigationBarActions: NavigationBarActions ) { @@ -41,9 +43,10 @@ fun HomeScreen( navigationBarActions = navigationBarActions, // TODO barAction = { FriendsAction() } ) { - BasicButton(R.string.start_session, Modifier.basicButton()) { - onStartSessionClick() - } + Feed(open) +// BasicButton(R.string.start_session, Modifier.basicButton()) { +// onStartSessionClick() +// } } } @@ -63,6 +66,7 @@ fun HomeScreenPreview() { HomeScreen( onStartSessionClick = {}, drawerActions = DrawerActions({}, {}, {}, {}, {}), - navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) + navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), + open = {} ) } From 49835108f3976e27b0c93e0b6ecbd7dc213e47b7 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:05:43 +0200 Subject: [PATCH 020/163] subject id and taskid as parameters for getSessionReport --- .../be/ugent/sel/studeez/screens/session/SessionViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt index 920d4e0..cd4c93a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt @@ -30,7 +30,7 @@ class SessionViewModel @Inject constructor( } fun endSession(openAndPopUp: (String, String) -> Unit) { - sessionReportState.sessionReport = getTimer().getSessionReport(task.id) + sessionReportState.sessionReport = getTimer().getSessionReport(task.subjectId, task.id) openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN) } } \ No newline at end of file From e1ddf41d09ef719983b723c2473dcf11c043d984 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:06:20 +0200 Subject: [PATCH 021/163] #74 added continue string for feed --- app/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d51259c..bc00963 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -29,6 +29,9 @@ Home Start session + + Continue + Tasks Task From 260171a972a1b858e66bd52bcfe9392cdb8c9577 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:25:18 +0200 Subject: [PATCH 022/163] #74 feedEntry now has an endTime --- .../java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt index 336a252..e24cd24 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt @@ -1,10 +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 totalStudyTime: Int = 0, + val endTime: Timestamp = Timestamp(0, 0) ) From 3f6d416f39446affee82ce284f0d3095b94257fc Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 14:26:07 +0200 Subject: [PATCH 023/163] #74 feed is now sorted on taskdates --- .../domain/implementation/FirebaseFeedDAO.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index c406b45..d742bf3 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -7,6 +7,7 @@ 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 @@ -22,15 +23,22 @@ class FirebaseFeedDAO @Inject constructor( .map { sessionReport -> sessionToFeedEntry(sessionReport) } .groupBy { it.taskId } .map { fuseFeedEntries(it.component2()) } + .sortedByDescending { it.endTime } } } private fun fuseFeedEntries(entries: List): FeedEntry = entries.fold(entries[0]) { accEntry, newEntry -> - val newStudyTime = accEntry.totalStudyTime + newEntry.totalStudyTime - accEntry.copy(totalStudyTime = newStudyTime) + 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 + } + private suspend fun sessionToFeedEntry(sessionReport: SessionReport): FeedEntry { val subjectId: String = sessionReport.subjectId @@ -45,7 +53,8 @@ class FirebaseFeedDAO @Inject constructor( taskName = task.name, taskId = task.id, subjectId = subject.id, - totalStudyTime = sessionReport.studyTime + totalStudyTime = sessionReport.studyTime, + endTime = sessionReport.endTime ) } } \ No newline at end of file From f3a8c93a3e23969759de35dcfc2193b17cde376f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 16:10:32 +0200 Subject: [PATCH 024/163] #74 getFeedentries now returns a map with date as key and as value the entries for that day --- app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt | 2 +- .../sel/studeez/domain/implementation/FirebaseFeedDAO.kt | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt index 05d4425..2d91781 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FeedDAO.kt @@ -5,6 +5,6 @@ import kotlinx.coroutines.flow.Flow interface FeedDAO { - fun getFeedEntries(): Flow> + fun getFeedEntries(): Flow>> } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index d742bf3..600153a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -1,5 +1,6 @@ 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 @@ -17,16 +18,21 @@ class FirebaseFeedDAO @Inject constructor( private val subjectDAO: FireBaseSubjectDAO ) : FeedDAO { - override fun getFeedEntries(): Flow> { + override fun getFeedEntries(): Flow>> { return sessionDAO.getSessions().map {sessionReports -> sessionReports .map { sessionReport -> sessionToFeedEntry(sessionReport) } .groupBy { it.taskId } .map { fuseFeedEntries(it.component2()) } .sortedByDescending { it.endTime } + .groupBy { getFormattedTime(it) } } } + private fun getFormattedTime(entry: FeedEntry): String { + return DateFormat.getDateInstance().format(entry.endTime.toDate()) + } + private fun fuseFeedEntries(entries: List): FeedEntry = entries.fold(entries[0]) { accEntry, newEntry -> accEntry.copy( From ac1bfe4d49a33ee4cc7ffc049fb0e292017134ea Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 16:10:56 +0200 Subject: [PATCH 025/163] #74 feed now had dates --- .../be/ugent/sel/studeez/screens/home/Feed.kt | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt index 0e93a96..62f285a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt @@ -1,5 +1,7 @@ package be.ugent.sel.studeez.screens.home +import android.icu.text.DateFormat +import android.icu.text.SimpleDateFormat import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -21,24 +23,37 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel 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.task.Subject import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds +import com.google.type.Date +import java.time.temporal.TemporalField +import java.util.* @Composable fun Feed( open: (String) -> Unit, viewModel: FeedViewModel = hiltViewModel() ) { - val feedEntries = viewModel.getFeedEntries().collectAsState(initial = emptyList()) + val feedEntries = viewModel.getFeedEntries().collectAsState(initial = emptyMap()) LazyColumn { - items(feedEntries.value) {feedEntry -> - FeedEntryCard(feedEntry = feedEntry) { - viewModel.continueWithTask(open, feedEntry.subjectId, feedEntry.taskId) + + items(feedEntries.value.toList()) {(date, feedEntries) -> + Text( + text = date, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + modifier = Modifier.padding(horizontal = 10.dp) + ) + feedEntries.forEach { feedEntry -> + FeedEntryCard(feedEntry = feedEntry) { + viewModel.continueWithTask(open, feedEntry.subjectId, feedEntry.taskId) + } } } } From 4e8fb28b0298cc99868c9e43ede74887fec9a16f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 16:11:46 +0200 Subject: [PATCH 026/163] #74 dao returns map --- .../java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt index 3b9fb0c..aca17c8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt @@ -21,9 +21,9 @@ class FeedViewModel @Inject constructor( logService: LogService ) : StudeezViewModel(logService) { - private val entries: Flow> = feedDAO.getFeedEntries() + private val entries: Flow>> = feedDAO.getFeedEntries() - fun getFeedEntries(): Flow> { + fun getFeedEntries(): Flow>> { return entries } From afe58572a6b97d4ca6b1aaff8d00c98d23900415 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 16:58:25 +0200 Subject: [PATCH 027/163] #74 fusing entries only on a daily basis --- .../sel/studeez/domain/implementation/FirebaseFeedDAO.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index 600153a..79e4e68 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -22,10 +22,14 @@ class FirebaseFeedDAO @Inject constructor( return sessionDAO.getSessions().map {sessionReports -> sessionReports .map { sessionReport -> sessionToFeedEntry(sessionReport) } - .groupBy { it.taskId } - .map { fuseFeedEntries(it.component2()) } .sortedByDescending { it.endTime } .groupBy { getFormattedTime(it) } + .mapValues { (_, entries) -> + entries + .groupBy { it.taskId } + .map { fuseFeedEntries(it.component2()) } + .sortedByDescending { it.endTime } + } } } From b5b32059718448a4c38a5e1596a9faca7fd5ebed Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 16:59:23 +0200 Subject: [PATCH 028/163] #74 UI Refactor --- .../be/ugent/sel/studeez/screens/home/Feed.kt | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt index 62f285a..4a254ac 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt @@ -1,7 +1,5 @@ package be.ugent.sel.studeez.screens.home -import android.icu.text.DateFormat -import android.icu.text.SimpleDateFormat import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -9,17 +7,13 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Card import androidx.compose.material.Divider -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.runtime.collectAsState 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.unit.dp @@ -27,12 +21,9 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.StealthButton +import be.ugent.sel.studeez.common.ext.spacer import be.ugent.sel.studeez.data.local.models.FeedEntry -import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds -import com.google.type.Date -import java.time.temporal.TemporalField -import java.util.* @Composable fun Feed( @@ -44,21 +35,41 @@ fun Feed( LazyColumn { items(feedEntries.value.toList()) {(date, feedEntries) -> - Text( - text = date, - fontWeight = FontWeight.Bold, - fontSize = 20.sp, - modifier = Modifier.padding(horizontal = 10.dp) - ) + 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 -> FeedEntryCard(feedEntry = feedEntry) { viewModel.continueWithTask(open, feedEntry.subjectId, feedEntry.taskId) } } + Spacer(modifier = Modifier.height(20.dp)) } } } +@Composable +fun DateText(date: String) { + Text( + text = date, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + modifier = Modifier.padding(horizontal = 10.dp) + ) +} + @Composable fun FeedEntryCard( feedEntry: FeedEntry, From d036afc319e20aec40f1ec5d681805d290366b14 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 17:10:57 +0200 Subject: [PATCH 029/163] Docs --- .../studeez/domain/implementation/FirebaseFeedDAO.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index 79e4e68..56d9000 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -18,6 +18,9 @@ class FirebaseFeedDAO @Inject constructor( 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>> { return sessionDAO.getSessions().map {sessionReports -> sessionReports @@ -28,7 +31,6 @@ class FirebaseFeedDAO @Inject constructor( entries .groupBy { it.taskId } .map { fuseFeedEntries(it.component2()) } - .sortedByDescending { it.endTime } } } } @@ -37,6 +39,10 @@ class FirebaseFeedDAO @Inject constructor( 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 = entries.fold(entries[0]) { accEntry, newEntry -> accEntry.copy( @@ -49,7 +55,9 @@ class FirebaseFeedDAO @Inject constructor( 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 From 9a214ce97cc7118396eb38f4b09496365c7b45ca Mon Sep 17 00:00:00 2001 From: lbarraga Date: Sun, 7 May 2023 17:23:43 +0200 Subject: [PATCH 030/163] change name of button function --- app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt index 4a254ac..7847ceb 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Card -import androidx.compose.material.Divider import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -21,7 +20,6 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.StealthButton -import be.ugent.sel.studeez.common.ext.spacer import be.ugent.sel.studeez.data.local.models.FeedEntry import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds @@ -73,7 +71,7 @@ fun DateText(date: String) { @Composable fun FeedEntryCard( feedEntry: FeedEntry, - onViewSubject: () -> Unit, + continueWithTask: () -> Unit, ) { Card( modifier = Modifier @@ -122,7 +120,7 @@ fun FeedEntryCard( .padding(start = 10.dp, end = 5.dp) .weight(1f) ) { - onViewSubject() + continueWithTask() } } } From c62d15ee99ca56608890885d10e599c24f2c1c7d Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 8 May 2023 17:10:44 +0200 Subject: [PATCH 031/163] comment in homescreen weg --- .../main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index b71ada1..0911fd9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -44,9 +44,6 @@ fun HomeScreen( // TODO barAction = { FriendsAction() } ) { Feed(open) -// BasicButton(R.string.start_session, Modifier.basicButton()) { -// onStartSessionClick() -// } } } From 9df80f4f935de6743143ce3b7079030715623288 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 8 May 2023 17:40:23 +0200 Subject: [PATCH 032/163] Fix: double counted entries --- .../ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index 56d9000..c116fbf 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -44,7 +44,7 @@ class FirebaseFeedDAO @Inject constructor( * feed-entry by adding the studytime and keeping the most recent end-timestamp */ private fun fuseFeedEntries(entries: List): FeedEntry = - entries.fold(entries[0]) { accEntry, newEntry -> + entries.drop(1).fold(entries[0]) { accEntry, newEntry -> accEntry.copy( totalStudyTime = accEntry.totalStudyTime + newEntry.totalStudyTime, endTime = getMostRecent(accEntry.endTime, newEntry.endTime) From 08c779030e3580f1084b990e8c094f3bcaf0c9f9 Mon Sep 17 00:00:00 2001 From: brreynie Date: Mon, 8 May 2023 21:05:54 +0200 Subject: [PATCH 033/163] refactor FeedEntry --- .../common/composable/TextComposable.kt | 13 +++ .../be/ugent/sel/studeez/screens/home/Feed.kt | 86 ++--------------- .../sel/studeez/screens/home/FeedEntry.kt | 96 +++++++++++++++++++ 3 files changed, 119 insertions(+), 76 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/home/FeedEntry.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextComposable.kt index 1b921a9..25fa3c4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextComposable.kt @@ -3,10 +3,13 @@ package be.ugent.sel.studeez.common.composable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable @@ -23,4 +26,14 @@ fun Headline( fontSize = 34.sp ) } +} + +@Composable +fun DateText(date: String) { + Text( + text = date, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + modifier = Modifier.padding(horizontal = 10.dp) + ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt index 7847ceb..af42203 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt @@ -1,26 +1,19 @@ package be.ugent.sel.studeez.screens.home -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Card import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState 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 androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel -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.common.composable.DateText import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds @Composable @@ -29,10 +22,10 @@ fun Feed( viewModel: FeedViewModel = hiltViewModel() ) { val feedEntries = viewModel.getFeedEntries().collectAsState(initial = emptyMap()) - + LazyColumn { - items(feedEntries.value.toList()) {(date, feedEntries) -> + items(feedEntries.value.toList()) { (date, feedEntries) -> Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier @@ -49,7 +42,7 @@ fun Feed( ) } feedEntries.forEach { feedEntry -> - FeedEntryCard(feedEntry = feedEntry) { + FeedEntry(feedEntry = feedEntry) { viewModel.continueWithTask(open, feedEntry.subjectId, feedEntry.taskId) } } @@ -58,70 +51,11 @@ fun Feed( } } +@Preview @Composable -fun DateText(date: String) { - Text( - text = date, - fontWeight = FontWeight.Bold, - fontSize = 20.sp, - modifier = Modifier.padding(horizontal = 10.dp) +fun FeedPreview() { + Feed( + open = {}, + viewModel = hiltViewModel(), ) -} - -@Composable -fun FeedEntryCard( - 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(3f) - ) { - Box( - modifier = Modifier - .size(20.dp) - .clip(CircleShape) - .background(Color(feedEntry.argb_color)), - ) - Column( - verticalArrangement = Arrangement.spacedBy(0.dp) - ) { - Text( - text = feedEntry.subJectName, - fontWeight = FontWeight.Bold, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - ) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text(text = feedEntry.taskName) - Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) - } - } - } - StealthButton( - text = R.string.start, - modifier = Modifier - .padding(start = 10.dp, end = 5.dp) - .weight(1f) - ) { - continueWithTask() - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedEntry.kt new file mode 100644 index 0000000..a7d1fc1 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedEntry.kt @@ -0,0 +1,96 @@ +package be.ugent.sel.studeez.screens.home + +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, + ) + ) +} \ No newline at end of file From d31276ef815a7e055717ea43a30a1639ebd9c13a Mon Sep 17 00:00:00 2001 From: brreynie Date: Mon, 8 May 2023 21:21:14 +0200 Subject: [PATCH 034/163] refactor Feed --- .../sel/studeez/navigation/StudeezNavGraph.kt | 4 +- .../be/ugent/sel/studeez/screens/home/Feed.kt | 43 +++++++++++-------- .../sel/studeez/screens/home/FeedViewModel.kt | 2 +- .../sel/studeez/screens/home/HomeScreen.kt | 17 ++++---- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index a09846a..bd12413 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -15,6 +15,7 @@ import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions import be.ugent.sel.studeez.screens.home.HomeRoute +import be.ugent.sel.studeez.screens.home.getFeedActions import be.ugent.sel.studeez.screens.log_in.LoginRoute import be.ugent.sel.studeez.screens.profile.EditProfileRoute import be.ugent.sel.studeez.screens.profile.ProfileRoute @@ -67,7 +68,8 @@ fun StudeezNavGraph( open, viewModel = hiltViewModel(), drawerActions = drawerActions, - navigationBarActions = navigationBarActions + navigationBarActions = navigationBarActions, + feedActions = getFeedActions(hiltViewModel(), open), ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt index af42203..96a9623 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt @@ -9,22 +9,40 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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 androidx.hilt.navigation.compose.hiltViewModel import be.ugent.sel.studeez.common.composable.DateText +import be.ugent.sel.studeez.data.local.models.FeedEntry import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds +import kotlinx.coroutines.flow.Flow + +data class FeedActions( + val getFeedEntries: () -> Flow>>, + val continueTask: (String, String) -> Unit, +) + +fun getFeedActions( + viewmodel: FeedViewModel, + open: (String) -> Unit, +): FeedActions { + return FeedActions( + getFeedEntries = viewmodel::getFeedEntries, + continueTask = { subjectId, taskId -> + viewmodel.continueTask( + open, + subjectId, + taskId, + ) + }, + ) +} @Composable fun Feed( - open: (String) -> Unit, - viewModel: FeedViewModel = hiltViewModel() + feedActions: FeedActions, ) { - val feedEntries = viewModel.getFeedEntries().collectAsState(initial = emptyMap()) - + val feedEntries = feedActions.getFeedEntries().collectAsState(initial = emptyMap()) LazyColumn { - items(feedEntries.value.toList()) { (date, feedEntries) -> Row( horizontalArrangement = Arrangement.SpaceBetween, @@ -43,19 +61,10 @@ fun Feed( } feedEntries.forEach { feedEntry -> FeedEntry(feedEntry = feedEntry) { - viewModel.continueWithTask(open, feedEntry.subjectId, feedEntry.taskId) + feedActions.continueTask(feedEntry.subjectId, feedEntry.taskId) } } Spacer(modifier = Modifier.height(20.dp)) } } -} - -@Preview -@Composable -fun FeedPreview() { - Feed( - open = {}, - viewModel = hiltViewModel(), - ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt index aca17c8..1f57175 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt @@ -27,7 +27,7 @@ class FeedViewModel @Inject constructor( return entries } - fun continueWithTask(open: (String) -> Unit, subjectId: String, taskId: String) { + fun continueTask(open: (String) -> Unit, subjectId: String, taskId: String) { viewModelScope.launch { val task = taskDAO.getTask(subjectId, taskId) selectedTask.set(task) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index 0911fd9..79f03e3 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -5,15 +5,13 @@ import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview 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.drawer.DrawerActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions -import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.resources +import kotlinx.coroutines.flow.flowOf @Composable fun HomeRoute( @@ -21,21 +19,22 @@ fun HomeRoute( viewModel: HomeViewModel, drawerActions: DrawerActions, navigationBarActions: NavigationBarActions, + feedActions: FeedActions, ) { HomeScreen( - onStartSessionClick = { viewModel.onStartSessionClick(open) }, drawerActions = drawerActions, open = open, navigationBarActions = navigationBarActions, + feedActions = feedActions, ) } @Composable fun HomeScreen( - onStartSessionClick: () -> Unit, open: (String) -> Unit, drawerActions: DrawerActions, - navigationBarActions: NavigationBarActions + navigationBarActions: NavigationBarActions, + feedActions: FeedActions, ) { PrimaryScreenTemplate( title = resources().getString(R.string.home), @@ -43,7 +42,7 @@ fun HomeScreen( navigationBarActions = navigationBarActions, // TODO barAction = { FriendsAction() } ) { - Feed(open) + Feed(feedActions) } } @@ -61,9 +60,9 @@ fun FriendsAction() { @Composable fun HomeScreenPreview() { HomeScreen( - onStartSessionClick = {}, drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), - open = {} + open = {}, + feedActions = FeedActions({ flowOf() }, { _, _ -> run {} }) ) } From 6ba0018765a4d09bb048d13db64d112c234cacb0 Mon Sep 17 00:00:00 2001 From: brreynie Date: Mon, 8 May 2023 21:51:18 +0200 Subject: [PATCH 035/163] refactor feed to use UiState.Loading --- .../studeez/common/composable/feed/Feed.kt | 99 +++++++++++++++++++ .../composable/feed}/FeedEntry.kt | 2 +- .../common/composable/feed/FeedUiState.kt | 8 ++ .../composable/feed}/FeedViewModel.kt | 14 ++- .../sel/studeez/navigation/StudeezNavGraph.kt | 4 +- .../be/ugent/sel/studeez/screens/home/Feed.kt | 70 ------------- .../sel/studeez/screens/home/HomeScreen.kt | 16 ++- .../sel/studeez/screens/home/HomeViewModel.kt | 19 ---- 8 files changed, 131 insertions(+), 101 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt rename app/src/main/java/be/ugent/sel/studeez/{screens/home => common/composable/feed}/FeedEntry.kt (98%) create mode 100644 app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedUiState.kt rename app/src/main/java/be/ugent/sel/studeez/{screens/home => common/composable/feed}/FeedViewModel.kt (75%) delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt new file mode 100644 index 0000000..6c7c0c1 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt @@ -0,0 +1,99 @@ +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.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.DateText +import be.ugent.sel.studeez.data.local.models.FeedEntry +import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +data class FeedActions( + val getFeedEntries: () -> Flow>>, + val continueTask: (String, String) -> Unit, +) + +fun getFeedActions( + viewmodel: FeedViewModel, + open: (String) -> Unit, +): FeedActions { + return FeedActions( + getFeedEntries = viewmodel::getFeedEntries, + continueTask = { subjectId, taskId -> + viewmodel.continueTask( + open, + subjectId, + taskId, + ) + }, + ) +} + +@Composable +fun Feed( + feedActions: FeedActions, + uiState: FeedUiState, +) { + when (uiState) { + FeedUiState.Loading -> { + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + CircularProgressIndicator(color = MaterialTheme.colors.onBackground) + } + } + is FeedUiState.Succes -> { +// val feedEntries = feedActions.getFeedEntries().collectAsState(initial = emptyMap()) + 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) { + feedActions.continueTask(feedEntry.subjectId, feedEntry.taskId) + } + } + Spacer(modifier = Modifier.height(20.dp)) + } + } + } + } +} + +@Preview +@Composable +fun FeedPreview() { + Feed( + feedActions = FeedActions({ flowOf() }, { _, _ -> run {} }), + uiState = FeedUiState.Loading, + ) +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt similarity index 98% rename from app/src/main/java/be/ugent/sel/studeez/screens/home/FeedEntry.kt rename to app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt index a7d1fc1..6dce710 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.home +package be.ugent.sel.studeez.common.composable.feed import androidx.compose.foundation.background import androidx.compose.foundation.layout.* diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedUiState.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedUiState.kt new file mode 100644 index 0000000..1b938ca --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedUiState.kt @@ -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>) : FeedUiState +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt similarity index 75% rename from app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt index 1f57175..d31d83a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/FeedViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.home +package be.ugent.sel.studeez.common.composable.feed import androidx.lifecycle.viewModelScope import be.ugent.sel.studeez.data.SelectedTask @@ -9,13 +9,13 @@ 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 kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class FeedViewModel @Inject constructor( - private val feedDAO: FeedDAO, + feedDAO: FeedDAO, private val taskDAO: TaskDAO, private val selectedTask: SelectedTask, logService: LogService @@ -23,6 +23,14 @@ class FeedViewModel @Inject constructor( private val entries: Flow>> = feedDAO.getFeedEntries() + val uiState: StateFlow = feedDAO.getFeedEntries() + .map { FeedUiState.Succes(it) } + .stateIn( + scope = viewModelScope, + initialValue = FeedUiState.Loading, + started = SharingStarted.Eagerly, + ) + fun getFeedEntries(): Flow>> { return entries } diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index bd12413..5becc44 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -15,7 +15,6 @@ import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions import be.ugent.sel.studeez.screens.home.HomeRoute -import be.ugent.sel.studeez.screens.home.getFeedActions import be.ugent.sel.studeez.screens.log_in.LoginRoute import be.ugent.sel.studeez.screens.profile.EditProfileRoute import be.ugent.sel.studeez.screens.profile.ProfileRoute @@ -66,10 +65,9 @@ fun StudeezNavGraph( composable(StudeezDestinations.HOME_SCREEN) { HomeRoute( open, - viewModel = hiltViewModel(), drawerActions = drawerActions, navigationBarActions = navigationBarActions, - feedActions = getFeedActions(hiltViewModel(), open), + feedViewModel = hiltViewModel(), ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt deleted file mode 100644 index 96a9623..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/Feed.kt +++ /dev/null @@ -1,70 +0,0 @@ -package be.ugent.sel.studeez.screens.home - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Alignment -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 be.ugent.sel.studeez.common.composable.DateText -import be.ugent.sel.studeez.data.local.models.FeedEntry -import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds -import kotlinx.coroutines.flow.Flow - -data class FeedActions( - val getFeedEntries: () -> Flow>>, - val continueTask: (String, String) -> Unit, -) - -fun getFeedActions( - viewmodel: FeedViewModel, - open: (String) -> Unit, -): FeedActions { - return FeedActions( - getFeedEntries = viewmodel::getFeedEntries, - continueTask = { subjectId, taskId -> - viewmodel.continueTask( - open, - subjectId, - taskId, - ) - }, - ) -} - -@Composable -fun Feed( - feedActions: FeedActions, -) { - val feedEntries = feedActions.getFeedEntries().collectAsState(initial = emptyMap()) - LazyColumn { - items(feedEntries.value.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) { - feedActions.continueTask(feedEntry.subjectId, feedEntry.taskId) - } - } - Spacer(modifier = Modifier.height(20.dp)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index 79f03e3..78d5ddb 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -5,10 +5,13 @@ import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.tooling.preview.Preview import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.drawer.DrawerActions +import be.ugent.sel.studeez.common.composable.feed.* import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions import be.ugent.sel.studeez.resources import kotlinx.coroutines.flow.flowOf @@ -16,16 +19,17 @@ import kotlinx.coroutines.flow.flowOf @Composable fun HomeRoute( open: (String) -> Unit, - viewModel: HomeViewModel, drawerActions: DrawerActions, navigationBarActions: NavigationBarActions, - feedActions: FeedActions, + feedViewModel: FeedViewModel, ) { + val feedUiState by feedViewModel.uiState.collectAsState() HomeScreen( drawerActions = drawerActions, open = open, navigationBarActions = navigationBarActions, - feedActions = feedActions, + feedActions = getFeedActions(feedViewModel, open), + feedUiState = feedUiState, ) } @@ -35,6 +39,7 @@ fun HomeScreen( drawerActions: DrawerActions, navigationBarActions: NavigationBarActions, feedActions: FeedActions, + feedUiState: FeedUiState, ) { PrimaryScreenTemplate( title = resources().getString(R.string.home), @@ -42,7 +47,7 @@ fun HomeScreen( navigationBarActions = navigationBarActions, // TODO barAction = { FriendsAction() } ) { - Feed(feedActions) + Feed(feedActions, feedUiState) } } @@ -63,6 +68,7 @@ fun HomeScreenPreview() { drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), open = {}, - feedActions = FeedActions({ flowOf() }, { _, _ -> run {} }) + feedActions = FeedActions({ flowOf() }, { _, _ -> run {} }), + feedUiState = FeedUiState.Loading, ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt deleted file mode 100644 index b27f995..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file From 366f236f9865136ce36206a084e938f4c0c14951 Mon Sep 17 00:00:00 2001 From: brreynie Date: Mon, 8 May 2023 22:14:17 +0200 Subject: [PATCH 036/163] add previews for feed --- .../studeez/common/composable/feed/Feed.kt | 70 +++++++++++-------- .../common/composable/feed/FeedViewModel.kt | 6 -- .../sel/studeez/screens/home/HomeScreen.kt | 45 ++++++++++-- 3 files changed, 80 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt index 6c7c0c1..5dac1c0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt @@ -16,34 +16,11 @@ import androidx.compose.ui.unit.sp import be.ugent.sel.studeez.common.composable.DateText import be.ugent.sel.studeez.data.local.models.FeedEntry import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - -data class FeedActions( - val getFeedEntries: () -> Flow>>, - val continueTask: (String, String) -> Unit, -) - -fun getFeedActions( - viewmodel: FeedViewModel, - open: (String) -> Unit, -): FeedActions { - return FeedActions( - getFeedEntries = viewmodel::getFeedEntries, - continueTask = { subjectId, taskId -> - viewmodel.continueTask( - open, - subjectId, - taskId, - ) - }, - ) -} @Composable fun Feed( - feedActions: FeedActions, uiState: FeedUiState, + continueTask: (String, String) -> Unit, ) { when (uiState) { FeedUiState.Loading -> { @@ -58,7 +35,6 @@ fun Feed( } } is FeedUiState.Succes -> { -// val feedEntries = feedActions.getFeedEntries().collectAsState(initial = emptyMap()) val feedEntries = uiState.feedEntries LazyColumn { items(feedEntries.toList()) { (date, feedEntries) -> @@ -79,7 +55,7 @@ fun Feed( } feedEntries.forEach { feedEntry -> FeedEntry(feedEntry = feedEntry) { - feedActions.continueTask(feedEntry.subjectId, feedEntry.taskId) + continueTask(feedEntry.subjectId, feedEntry.taskId) } } Spacer(modifier = Modifier.height(20.dp)) @@ -89,11 +65,49 @@ fun Feed( } } +@Preview +@Composable +fun FeedLoadingPreview() { + Feed( + uiState = FeedUiState.Loading, + continueTask = { _, _ -> run {} }, + ) +} + @Preview @Composable fun FeedPreview() { Feed( - feedActions = FeedActions({ flowOf() }, { _, _ -> run {} }), - uiState = FeedUiState.Loading, + 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 {} }, ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt index d31d83a..443d0c9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt @@ -21,8 +21,6 @@ class FeedViewModel @Inject constructor( logService: LogService ) : StudeezViewModel(logService) { - private val entries: Flow>> = feedDAO.getFeedEntries() - val uiState: StateFlow = feedDAO.getFeedEntries() .map { FeedUiState.Succes(it) } .stateIn( @@ -31,10 +29,6 @@ class FeedViewModel @Inject constructor( started = SharingStarted.Eagerly, ) - fun getFeedEntries(): Flow>> { - return entries - } - fun continueTask(open: (String) -> Unit, subjectId: String, taskId: String) { viewModelScope.launch { val task = taskDAO.getTask(subjectId, taskId) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index 78d5ddb..dad3dd2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -11,10 +11,12 @@ import androidx.compose.ui.tooling.preview.Preview import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.drawer.DrawerActions -import be.ugent.sel.studeez.common.composable.feed.* +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.data.local.models.FeedEntry import be.ugent.sel.studeez.resources -import kotlinx.coroutines.flow.flowOf @Composable fun HomeRoute( @@ -28,8 +30,8 @@ fun HomeRoute( drawerActions = drawerActions, open = open, navigationBarActions = navigationBarActions, - feedActions = getFeedActions(feedViewModel, open), feedUiState = feedUiState, + continueTask = { subjectId, taskId -> feedViewModel.continueTask(open, subjectId, taskId) }, ) } @@ -38,8 +40,8 @@ fun HomeScreen( open: (String) -> Unit, drawerActions: DrawerActions, navigationBarActions: NavigationBarActions, - feedActions: FeedActions, feedUiState: FeedUiState, + continueTask: (String, String) -> Unit, ) { PrimaryScreenTemplate( title = resources().getString(R.string.home), @@ -47,7 +49,7 @@ fun HomeScreen( navigationBarActions = navigationBarActions, // TODO barAction = { FriendsAction() } ) { - Feed(feedActions, feedUiState) + Feed(feedUiState, continueTask) } } @@ -68,7 +70,36 @@ fun HomeScreenPreview() { drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), open = {}, - feedActions = FeedActions({ flowOf() }, { _, _ -> run {} }), - feedUiState = FeedUiState.Loading, + 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 {} }, ) } From 5f8b7d7a7b653ed2d56d842327261b001d8d7f1d Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 15:30:37 +0200 Subject: [PATCH 037/163] show #tasks and #completedTasks in subject entry --- .../common/composable/tasks/SubjectEntry.kt | 18 +++++++--------- .../studeez/data/local/models/task/Subject.kt | 7 ++++++- .../studeez/data/local/models/task/Task.kt | 2 ++ .../be/ugent/sel/studeez/domain/TaskDAO.kt | 4 ++++ .../implementation/FireBaseSubjectDAO.kt | 10 ++++++++- .../domain/implementation/FireBaseTaskDAO.kt | 21 ++++++++++++++++++- .../domain/implementation/FirebaseFeedDAO.kt | 14 +++++++------ .../studeez/screens/tasks/SubjectScreen.kt | 2 +- 8 files changed, 57 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt index 8655ba3..a6b89bf 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt @@ -1,13 +1,7 @@ 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.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Card import androidx.compose.material.Icon @@ -24,10 +18,10 @@ 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 +import be.ugent.sel.studeez.R.string as AppText @Composable fun SubjectEntry( @@ -80,7 +74,7 @@ fun SubjectEntry( imageVector = Icons.Default.List, contentDescription = stringResource(id = AppText.tasks) ) - Text(text = "0/0") // TODO + Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") // TODO } } } @@ -104,7 +98,9 @@ fun SubjectEntryPreview() { subject = Subject( name = "Test Subject", argb_color = 0xFFFFD200, - time = 60 + time = 60, + taskCount = 5, + taskCompletedCount = 2, ), ) {} } @@ -116,7 +112,7 @@ fun OverflowSubjectEntryPreview() { subject = Subject( name = "Testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", argb_color = 0xFFFFD200, - time = 60 + time = 60, ), ) {} } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt index e84c2bb..622ef3c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt @@ -1,10 +1,15 @@ package be.ugent.sel.studeez.data.local.models.task import com.google.firebase.firestore.DocumentId +import com.google.firebase.firestore.Exclude data class Subject( @DocumentId val id: String = "", val name: String = "", val time: Int = 0, val argb_color: Long = 0, -) \ No newline at end of file + @get:Exclude @set:Exclude + var taskCount: Int = 0, + @get:Exclude @set:Exclude + var taskCompletedCount: Int = 0, +) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt index f2618db..91242b0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt @@ -8,6 +8,7 @@ data class Task( val completed: Boolean = false, val time: Int = 0, val subjectId: String = "", + val archived: Boolean = false, ) object TaskDocument { @@ -16,4 +17,5 @@ object TaskDocument { const val completed = "completed" const val time = "time" const val subjectId = "subjectId" + const val archived = "archived" } diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt index 988a10d..4c7ee44 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt @@ -17,4 +17,8 @@ interface TaskDAO { fun toggleTaskCompleted(task: Task, completed: Boolean) suspend fun getTask(subjectId: String, taskId: String): Task + + suspend fun getTaskCount(subject: Subject): Int + + suspend fun getCompletedTaskCount(subject: Subject): Int } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt index e022863..9156771 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt @@ -1,9 +1,9 @@ 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.domain.AccountDAO import be.ugent.sel.studeez.domain.SubjectDAO +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 @@ -16,11 +16,19 @@ import javax.inject.Inject class FireBaseSubjectDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO, + private val taskDAO: TaskDAO, ) : SubjectDAO { override fun getSubjects(): Flow> { return currentUserSubjectsCollection() .snapshots() .map { it.toObjects(Subject::class.java) } + .map { subjects -> + subjects.map { subject -> + subject.taskCount = taskDAO.getTaskCount(subject) + subject.taskCompletedCount = taskDAO.getCompletedTaskCount(subject) + subject + } + } } override suspend fun getSubject(subjectId: String): Subject? { diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt index 963c93b..bf32776 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt @@ -5,12 +5,12 @@ 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.AggregateSource import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.FirebaseFirestore 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.tasks.await import javax.inject.Inject @@ -21,6 +21,7 @@ class FireBaseTaskDAO @Inject constructor( ) : TaskDAO { override fun getTasks(subject: Subject): Flow> { return selectedSubjectTasksCollection(subject.id) + .whereEqualTo(TaskDocument.archived, false) .snapshots() .map { it.toObjects(Task::class.java) } } @@ -29,6 +30,24 @@ class FireBaseTaskDAO @Inject constructor( return selectedSubjectTasksCollection(subjectId).document(taskId).get().await().toObject()!! } + override suspend fun getTaskCount(subject: Subject): Int { + return selectedSubjectTasksCollection(subject.id) + .count() + .get(AggregateSource.SERVER) + .await() + .count.toInt() + } + + override suspend fun getCompletedTaskCount(subject: Subject): Int { + return selectedSubjectTasksCollection(subject.id) + .whereEqualTo(TaskDocument.completed, true) + .whereEqualTo(TaskDocument.archived, false) + .count() + .get(AggregateSource.SERVER) + .await() + .count.toInt() + } + override fun saveTask(newTask: Task) { selectedSubjectTasksCollection(newTask.subjectId).add(newTask) } diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index c116fbf..fa579bb 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -7,30 +7,32 @@ 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.SubjectDAO import be.ugent.sel.studeez.domain.TaskDAO import com.google.firebase.Timestamp -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import javax.inject.Inject class FirebaseFeedDAO @Inject constructor( private val sessionDAO: SessionDAO, private val taskDAO: TaskDAO, - private val subjectDAO: FireBaseSubjectDAO + private val subjectDAO: SubjectDAO ) : FeedDAO { /** * Return a map as with key the day and value a list of feedentries for that day. */ override fun getFeedEntries(): Flow>> { - return sessionDAO.getSessions().map {sessionReports -> + return sessionDAO.getSessions().map { sessionReports -> sessionReports - .map { sessionReport -> sessionToFeedEntry(sessionReport) } + .map { sessionReport -> sessionToFeedEntry(sessionReport) } .sortedByDescending { it.endTime } .groupBy { getFormattedTime(it) } .mapValues { (_, entries) -> entries - .groupBy { it.taskId } - .map { fuseFeedEntries(it.component2()) } + .groupBy { it.taskId } + .map { fuseFeedEntries(it.component2()) } } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt index 15a3925..a3395ea 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt @@ -54,6 +54,7 @@ fun SubjectScreen( Column( modifier = Modifier.padding(top = 5.dp) ) { + NewTaskSubjectButton(onClick = addSubject, AppText.new_subject) LazyColumn { items(subjects.value) { SubjectEntry( @@ -62,7 +63,6 @@ fun SubjectScreen( ) } } - NewTaskSubjectButton(onClick = addSubject, AppText.new_subject) } } } From e35f1438547925c25067580fd1e5dc83c33dc59f Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 16:00:15 +0200 Subject: [PATCH 038/163] archive tasks --- .../common/composable/tasks/TaskEntry.kt | 18 ++++-------------- .../sel/studeez/data/local/models/task/Task.kt | 8 ++++++-- .../domain/implementation/FireBaseTaskDAO.kt | 6 ++++-- .../sel/studeez/screens/tasks/TaskScreen.kt | 11 +++++++---- .../sel/studeez/screens/tasks/TaskViewModel.kt | 6 ++++++ 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt index bf3a7cf..35e7a44 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/TaskEntry.kt @@ -1,17 +1,7 @@ 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.foundation.layout.* +import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.runtime.Composable @@ -31,7 +21,7 @@ import be.ugent.sel.studeez.resources fun TaskEntry( task: Task, onCheckTask: (Boolean) -> Unit, - onDeleteTask: () -> Unit, + onArchiveTask: () -> Unit, onStartTask: () -> Unit ) { Card( @@ -81,7 +71,7 @@ fun TaskEntry( Box(modifier = Modifier.weight(7f)) { if (task.completed) { IconButton( - onClick = onDeleteTask, + onClick = onArchiveTask, modifier = Modifier .padding(start = 20.dp) ) { diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt index 91242b0..44d6d01 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt @@ -8,8 +8,12 @@ data class Task( val completed: Boolean = false, val time: Int = 0, val subjectId: String = "", - val archived: Boolean = false, -) + var archived: Boolean = false, +) { + fun archive() { + this.archived = true + } +} object TaskDocument { const val id = "id" diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt index bf32776..4443b87 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt @@ -32,6 +32,7 @@ class FireBaseTaskDAO @Inject constructor( override suspend fun getTaskCount(subject: Subject): Int { return selectedSubjectTasksCollection(subject.id) + .whereEqualTo(TaskDocument.archived, false) .count() .get(AggregateSource.SERVER) .await() @@ -53,7 +54,7 @@ class FireBaseTaskDAO @Inject constructor( } override fun updateTask(newTask: Task) { - selectedSubjectTasksCollection(newTask.id).document(newTask.id).set(newTask) + selectedSubjectTasksCollection(newTask.subjectId).document(newTask.id).set(newTask) } override fun deleteTask(oldTask: Task) { @@ -63,7 +64,8 @@ class FireBaseTaskDAO @Inject constructor( override fun toggleTaskCompleted(task: Task, completed: Boolean) { selectedSubjectTasksCollection(task.subjectId) .document(task.id) - .update(TaskDocument.completed, completed) +// .update(TaskDocument.completed, completed) + .set(task.copy(completed = completed)) } private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference = diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt index 8a35717..1b56bc6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt @@ -30,7 +30,8 @@ data class TaskActions( val deleteTask: (Task) -> Unit, val onCheckTask: (Task, Boolean) -> Unit, val editSubject: () -> Unit, - val startTask: (Task) -> Unit + val startTask: (Task) -> Unit, + val archiveTask: (Task) -> Unit, ) fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskActions { @@ -41,7 +42,8 @@ fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskAction deleteTask = viewModel::deleteTask, onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) }, editSubject = { viewModel.editSubject(open) }, - startTask = { task -> viewModel.startTask(task, open) } + startTask = { task -> viewModel.startTask(task, open) }, + archiveTask = viewModel::archiveTask ) } @@ -76,7 +78,7 @@ fun TaskScreen( TaskEntry( task = it, onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, - onDeleteTask = { taskActions.deleteTask(it) }, + onArchiveTask = { taskActions.archiveTask(it) }, onStartTask = { taskActions.startTask(it) } ) } @@ -111,7 +113,8 @@ fun TaskScreenPreview() { {}, { _, _ -> run {} }, {}, - {} + {}, + {}, ) ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt index 37f1c91..2361399 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.screens.tasks +import android.util.Log import be.ugent.sel.studeez.data.SelectedSubject import be.ugent.sel.studeez.data.SelectedTask import be.ugent.sel.studeez.data.local.models.task.Subject @@ -42,6 +43,11 @@ class TaskViewModel @Inject constructor( taskDAO.deleteTask(task) } + fun archiveTask(task: Task) { + task.archive() + taskDAO.updateTask(task) + } + fun toggleTaskCompleted(task: Task, completed: Boolean) { taskDAO.toggleTaskCompleted(task, completed) } From 1293ea31133ee40dde21a60e37b2ad9c39e377af Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 9 May 2023 16:17:10 +0200 Subject: [PATCH 039/163] #74 refactor feed --- .../studeez/common/composable/feed/Feed.kt | 115 ++++++++++++------ 1 file changed, 81 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt index 5dac1c0..54be2ea 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/Feed.kt @@ -9,58 +9,105 @@ 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 -> { - Column( + 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() - .fillMaxHeight(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + .padding(10.dp), + verticalAlignment = Alignment.CenterVertically ) { - CircularProgressIndicator(color = MaterialTheme.colors.onBackground) + val totalDayStudyTime: Int = feedEntries.sumOf { it.totalStudyTime } + DateText(date = date) + Text( + text = "${HoursMinutesSeconds(totalDayStudyTime)}", + fontSize = 15.sp, + fontWeight = FontWeight.Bold + ) } - } - is FeedUiState.Succes -> { - 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)) + 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, + ) } } } @@ -70,7 +117,7 @@ fun Feed( fun FeedLoadingPreview() { Feed( uiState = FeedUiState.Loading, - continueTask = { _, _ -> run {} }, + continueTask = { _, _ -> run {} }, {} ) } @@ -108,6 +155,6 @@ fun FeedPreview() { ) ) ), - continueTask = { _, _ -> run {} }, + continueTask = { _, _ -> run {} }, {} ) } \ No newline at end of file From 0fb3125f6a4ce5d33e8c42fbde874b0deb38dc05 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 9 May 2023 16:17:46 +0200 Subject: [PATCH 040/163] #74 button to add subject when user has empty feed --- .../ugent/sel/studeez/common/composable/feed/FeedViewModel.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt index 443d0c9..fbd32f4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt @@ -36,4 +36,8 @@ class FeedViewModel @Inject constructor( open(StudeezDestinations.TIMER_SELECTION_SCREEN) } } + + fun onEmptyFeedHelp(open: (String) -> Unit) { + open(StudeezDestinations.ADD_SUBJECT_FORM) + } } \ No newline at end of file From 9bf77b556c19c5a0bccd75d3ddfed396623e96a1 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 9 May 2023 16:19:03 +0200 Subject: [PATCH 041/163] added function that opens subject add screen --- .../java/be/ugent/sel/studeez/screens/home/HomeScreen.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index dad3dd2..c93527b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -32,6 +32,7 @@ fun HomeRoute( navigationBarActions = navigationBarActions, feedUiState = feedUiState, continueTask = { subjectId, taskId -> feedViewModel.continueTask(open, subjectId, taskId) }, + onEmptyFeedHelp = { feedViewModel.onEmptyFeedHelp(open) } ) } @@ -42,6 +43,7 @@ fun HomeScreen( navigationBarActions: NavigationBarActions, feedUiState: FeedUiState, continueTask: (String, String) -> Unit, + onEmptyFeedHelp: () -> Unit, ) { PrimaryScreenTemplate( title = resources().getString(R.string.home), @@ -49,7 +51,7 @@ fun HomeScreen( navigationBarActions = navigationBarActions, // TODO barAction = { FriendsAction() } ) { - Feed(feedUiState, continueTask) + Feed(feedUiState, continueTask, onEmptyFeedHelp) } } @@ -101,5 +103,6 @@ fun HomeScreenPreview() { ) ), continueTask = { _, _ -> run {} }, + onEmptyFeedHelp = {} ) } From 5be8432b18119ebf3b13465184cd0279fb478afe Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 9 May 2023 16:19:38 +0200 Subject: [PATCH 042/163] edit button on the right --- .../screens/timer_overview/TimerOverviewScreen.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt index a07dd67..3c25ddf 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt @@ -82,12 +82,13 @@ fun TimerOverviewScreen( items(timers.value) { timerInfo -> TimerEntry( timerInfo = timerInfo, - ) { - StealthButton( - text = R.string.edit, - onClick = { timerOverviewActions.onEditClick(timerInfo) } - ) - } + rightButton = { + StealthButton( + text = R.string.edit, + onClick = { timerOverviewActions.onEditClick(timerInfo) } + ) + } + ) } From 4fa7f97513d3cc00e79f3fc9d5643931b1bce4f6 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 9 May 2023 16:20:39 +0200 Subject: [PATCH 043/163] added padding to custom timer time picker button --- .../screens/timer_selection/TimerSelectionScreen.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt index 2f17e65..d78b4bf 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt @@ -1,10 +1,13 @@ 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.items import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Modifier 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.SecondaryScreenTemplate import be.ugent.sel.studeez.common.composable.StealthButton @@ -99,7 +102,10 @@ fun CustomTimerEntry( ) }, rightButton = { - TimePickerButton(initialSeconds = hms.getTotalSeconds()) { chosenTime -> + TimePickerButton( + initialSeconds = hms.getTotalSeconds(), + modifier = Modifier.padding(horizontal = 5.dp) + ) { chosenTime -> timerInfo.studyTime = chosenTime } } From 5992abf9c767ecbbec01b81888dcca4486366f7e Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 9 May 2023 16:21:08 +0200 Subject: [PATCH 044/163] default of custom timer is 1 hour --- .../screens/timer_selection/TimerSelectionViewModel.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt index ab42973..a4f646d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember 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.domain.LogService import be.ugent.sel.studeez.domain.TimerDAO @@ -21,7 +22,9 @@ class TimerSelectionViewModel @Inject constructor( logService: LogService ) : StudeezViewModel(logService) { - var customTimerStudyTime: MutableState = mutableStateOf(0) + var customTimerStudyTime: MutableState = mutableStateOf( + HoursMinutesSeconds(1, 0, 0).getTotalSeconds() + ) fun getAllTimers() : Flow> { return timerDAO.getAllTimers() From 529adbcab322797c29ed0943cc6ba2b94a8bb1a9 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 9 May 2023 16:21:37 +0200 Subject: [PATCH 045/163] added feed string --- app/src/main/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bc00963..d58be52 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,8 @@ Continue + This is your feed + Create you first subject and tasks to get started Tasks From 8a395fbd4e71400e22785318650bae4f064b65de Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 20:30:15 +0200 Subject: [PATCH 046/163] implement TaskDAO as a very simple fluent interface --- .../domain/implementation/FireBaseTaskDAO.kt | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt index 4443b87..049bb54 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt @@ -8,6 +8,7 @@ import be.ugent.sel.studeez.domain.TaskDAO import com.google.firebase.firestore.AggregateSource import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.Query import com.google.firebase.firestore.ktx.snapshots import com.google.firebase.firestore.ktx.toObject import kotlinx.coroutines.flow.Flow @@ -21,7 +22,7 @@ class FireBaseTaskDAO @Inject constructor( ) : TaskDAO { override fun getTasks(subject: Subject): Flow> { return selectedSubjectTasksCollection(subject.id) - .whereEqualTo(TaskDocument.archived, false) + .nonArchived() .snapshots() .map { it.toObjects(Task::class.java) } } @@ -32,7 +33,7 @@ class FireBaseTaskDAO @Inject constructor( override suspend fun getTaskCount(subject: Subject): Int { return selectedSubjectTasksCollection(subject.id) - .whereEqualTo(TaskDocument.archived, false) + .nonArchived() .count() .get(AggregateSource.SERVER) .await() @@ -41,8 +42,8 @@ class FireBaseTaskDAO @Inject constructor( override suspend fun getCompletedTaskCount(subject: Subject): Int { return selectedSubjectTasksCollection(subject.id) - .whereEqualTo(TaskDocument.completed, true) - .whereEqualTo(TaskDocument.archived, false) + .completed() + .nonArchived() .count() .get(AggregateSource.SERVER) .await() @@ -74,4 +75,19 @@ class FireBaseTaskDAO @Inject constructor( .collection(FireBaseCollections.SUBJECT_COLLECTION) .document(subjectId) .collection(FireBaseCollections.TASK_COLLECTION) + + // Extend CollectionReference and Query with some filters + + private fun CollectionReference.nonArchived(): Query = + this.whereEqualTo(TaskDocument.archived, false) + + private fun Query.nonArchived(): Query = + this.whereEqualTo(TaskDocument.archived, false) + + private fun CollectionReference.completed(): Query = + this.whereEqualTo(TaskDocument.completed, true) + + private fun Query.completed(): Query = + this.whereEqualTo(TaskDocument.completed, true) + } \ No newline at end of file From a60422e00480496754e31e123f652bf1b9d237fd Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 21:01:19 +0200 Subject: [PATCH 047/163] refactor DAO's --- .../be/ugent/sel/studeez/domain/SubjectDAO.kt | 5 ++ .../be/ugent/sel/studeez/domain/TaskDAO.kt | 6 --- .../implementation/FireBaseSubjectDAO.kt | 36 ++++++++++++- .../domain/implementation/FireBaseTaskDAO.kt | 51 +++++-------------- 4 files changed, 52 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt index da6abbe..bad8106 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt @@ -12,5 +12,10 @@ interface SubjectDAO { fun deleteSubject(oldSubject: Subject) fun updateSubject(newSubject: Subject) + + suspend fun getTaskCount(subject: Subject): Int + suspend fun getCompletedTaskCount(subject: Subject): Int + fun getStudyTime(subject: Subject): Flow + suspend fun getSubject(subjectId: String): Subject? } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt index 4c7ee44..8a2dd41 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/TaskDAO.kt @@ -14,11 +14,5 @@ interface TaskDAO { fun deleteTask(oldTask: Task) - fun toggleTaskCompleted(task: Task, completed: Boolean) - suspend fun getTask(subjectId: String, taskId: String): Task - - suspend fun getTaskCount(subject: Subject): Int - - suspend fun getCompletedTaskCount(subject: Subject): Int } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt index 9156771..3e9ae13 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt @@ -4,6 +4,7 @@ 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 be.ugent.sel.studeez.domain.TaskDAO +import com.google.firebase.firestore.AggregateSource import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots @@ -24,8 +25,8 @@ class FireBaseSubjectDAO @Inject constructor( .map { it.toObjects(Subject::class.java) } .map { subjects -> subjects.map { subject -> - subject.taskCount = taskDAO.getTaskCount(subject) - subject.taskCompletedCount = taskDAO.getCompletedTaskCount(subject) + subject.taskCount = getTaskCount(subject) + subject.taskCompletedCount = getCompletedTaskCount(subject) subject } } @@ -47,8 +48,39 @@ class FireBaseSubjectDAO @Inject constructor( currentUserSubjectsCollection().document(newSubject.id).set(newSubject) } + override suspend fun getTaskCount(subject: Subject): Int { + return subjectTasksCollection(subject) + .nonArchived() + .count() + .get(AggregateSource.SERVER) + .await() + .count.toInt() + } + + override suspend fun getCompletedTaskCount(subject: Subject): Int { + return subjectTasksCollection(subject) + .nonArchived() + .completed() + .count() + .get(AggregateSource.SERVER) + .await() + .count.toInt() + } + + override fun getStudyTime(subject: Subject): Flow { + return taskDAO.getTasks(subject) + .map { tasks -> tasks.sumOf { it.time } } + } + private fun currentUserSubjectsCollection(): CollectionReference = firestore.collection(FireBaseCollections.USER_COLLECTION) .document(auth.currentUserId) .collection(FireBaseCollections.SUBJECT_COLLECTION) + + private fun subjectTasksCollection(subject: Subject): CollectionReference = + firestore.collection(FireBaseCollections.USER_COLLECTION) + .document(auth.currentUserId) + .collection(FireBaseCollections.SUBJECT_COLLECTION) + .document(subject.id) + .collection(FireBaseCollections.TASK_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt index 049bb54..bde5c6f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt @@ -5,7 +5,6 @@ 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.AggregateSource import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query @@ -31,44 +30,20 @@ class FireBaseTaskDAO @Inject constructor( return selectedSubjectTasksCollection(subjectId).document(taskId).get().await().toObject()!! } - override suspend fun getTaskCount(subject: Subject): Int { - return selectedSubjectTasksCollection(subject.id) - .nonArchived() - .count() - .get(AggregateSource.SERVER) - .await() - .count.toInt() - } - - override suspend fun getCompletedTaskCount(subject: Subject): Int { - return selectedSubjectTasksCollection(subject.id) - .completed() - .nonArchived() - .count() - .get(AggregateSource.SERVER) - .await() - .count.toInt() - } - override fun saveTask(newTask: Task) { selectedSubjectTasksCollection(newTask.subjectId).add(newTask) } override fun updateTask(newTask: Task) { - selectedSubjectTasksCollection(newTask.subjectId).document(newTask.id).set(newTask) + selectedSubjectTasksCollection(newTask.subjectId) + .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) - .set(task.copy(completed = completed)) - } - private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference = firestore.collection(FireBaseCollections.USER_COLLECTION) .document(auth.currentUserId) @@ -76,18 +51,18 @@ class FireBaseTaskDAO @Inject constructor( .document(subjectId) .collection(FireBaseCollections.TASK_COLLECTION) - // Extend CollectionReference and Query with some filters +} - private fun CollectionReference.nonArchived(): Query = - this.whereEqualTo(TaskDocument.archived, false) +// Extend CollectionReference and Query with some filters - private fun Query.nonArchived(): Query = - this.whereEqualTo(TaskDocument.archived, false) +fun CollectionReference.nonArchived(): Query = + this.whereEqualTo(TaskDocument.archived, false) - private fun CollectionReference.completed(): Query = - this.whereEqualTo(TaskDocument.completed, true) +fun Query.nonArchived(): Query = + this.whereEqualTo(TaskDocument.archived, false) - private fun Query.completed(): Query = - this.whereEqualTo(TaskDocument.completed, true) +fun CollectionReference.completed(): Query = + this.whereEqualTo(TaskDocument.completed, true) -} \ No newline at end of file +fun Query.completed(): Query = + this.whereEqualTo(TaskDocument.completed, true) From ec31e116b4c3e0bf5bcded46a7ce9fa35c3770ee Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 21:29:52 +0200 Subject: [PATCH 048/163] refactor subjectscreen to use uiState --- .../common/composable/feed/FeedViewModel.kt | 6 +- .../studeez/data/local/models/task/Subject.kt | 3 +- .../studeez/data/local/models/task/Task.kt | 2 +- .../studeez/screens/tasks/SubjectScreen.kt | 76 ++++++++++++++----- .../studeez/screens/tasks/SubjectUiState.kt | 8 ++ .../studeez/screens/tasks/SubjectViewModel.kt | 18 +++-- .../studeez/screens/tasks/TaskViewModel.kt | 13 +--- 7 files changed, 84 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectUiState.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt index 443d0c9..13d134b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedViewModel.kt @@ -2,14 +2,16 @@ 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.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt index 622ef3c..6f29e22 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt @@ -6,10 +6,11 @@ import com.google.firebase.firestore.Exclude data class Subject( @DocumentId val id: String = "", val name: String = "", - val time: Int = 0, val argb_color: Long = 0, @get:Exclude @set:Exclude var taskCount: Int = 0, @get:Exclude @set:Exclude var taskCompletedCount: Int = 0, + @get:Exclude @set:Exclude + var time: Int = 0, ) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt index 44d6d01..e42ce5d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt @@ -5,7 +5,7 @@ import com.google.firebase.firestore.DocumentId data class Task( @DocumentId val id: String = "", val name: String = "", - val completed: Boolean = false, + var completed: Boolean = false, val time: Int = 0, val subjectId: String = "", var archived: Boolean = false, diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt index a3395ea..115c75e 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt @@ -1,11 +1,14 @@ package be.ugent.sel.studeez.screens.tasks -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding +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.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -16,8 +19,6 @@ 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 @@ -27,12 +28,13 @@ fun SubjectRoute( drawerActions: DrawerActions, navigationBarActions: NavigationBarActions, ) { + val uiState by viewModel.uiState.collectAsState() SubjectScreen( drawerActions = drawerActions, navigationBarActions = navigationBarActions, - addSubject = { viewModel.addSubject(open) }, - getSubjects = viewModel::getSubjects, + onAddSubject = { viewModel.onAddSubject(open) }, onViewSubject = { viewModel.onViewSubject(it, open) }, + uiState, ) } @@ -40,9 +42,9 @@ fun SubjectRoute( fun SubjectScreen( drawerActions: DrawerActions, navigationBarActions: NavigationBarActions, - addSubject: () -> Unit, - getSubjects: () -> Flow>, + onAddSubject: () -> Unit, onViewSubject: (Subject) -> Unit, + uiState: SubjectUiState, ) { PrimaryScreenTemplate( title = stringResource(AppText.my_subjects), @@ -50,17 +52,29 @@ fun SubjectScreen( navigationBarActions = navigationBarActions, barAction = {}, ) { - val subjects = getSubjects().collectAsState(initial = emptyList()) - Column( - modifier = Modifier.padding(top = 5.dp) - ) { - NewTaskSubjectButton(onClick = addSubject, AppText.new_subject) - LazyColumn { - items(subjects.value) { - SubjectEntry( - subject = it, - onViewSubject = { onViewSubject(it) }, - ) + when (uiState) { + SubjectUiState.Loading -> Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + CircularProgressIndicator(color = MaterialTheme.colors.onBackground) + } + is SubjectUiState.Succes -> { + Column( + modifier = Modifier.padding(top = 5.dp) + ) { + NewTaskSubjectButton(onClick = onAddSubject, AppText.new_subject) + LazyColumn { + items(uiState.subjects) { + SubjectEntry( + subject = it, + onViewSubject = { onViewSubject(it) }, + ) + } + } } } } @@ -73,8 +87,28 @@ fun SubjectScreenPreview() { SubjectScreen( drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), - addSubject = {}, - getSubjects = { flowOf() }, + onAddSubject = {}, onViewSubject = {}, + uiState = SubjectUiState.Succes( + listOf( + Subject( + name = "Test Subject", + argb_color = 0xFFFFD200, + taskCount = 5, taskCompletedCount = 2, + ) + ) + ) + ) +} + +@Preview +@Composable +fun SubjectScreenLoadingPreview() { + SubjectScreen( + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), + onAddSubject = {}, + onViewSubject = {}, + uiState = SubjectUiState.Loading ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectUiState.kt new file mode 100644 index 0000000..38adae4 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectUiState.kt @@ -0,0 +1,8 @@ +package be.ugent.sel.studeez.screens.tasks + +import be.ugent.sel.studeez.data.local.models.task.Subject + +sealed interface SubjectUiState { + object Loading : SubjectUiState + data class Succes(val subjects: List) : SubjectUiState +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt index f1d6071..8587327 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.screens.tasks +import androidx.lifecycle.viewModelScope import be.ugent.sel.studeez.data.SelectedSubject import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.domain.LogService @@ -7,7 +8,7 @@ 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 kotlinx.coroutines.flow.* import javax.inject.Inject @HiltViewModel @@ -16,12 +17,17 @@ class SubjectViewModel @Inject constructor( private val selectedSubject: SelectedSubject, logService: LogService, ) : StudeezViewModel(logService) { - fun addSubject(open: (String) -> Unit) { - open(StudeezDestinations.ADD_SUBJECT_FORM) - } - fun getSubjects(): Flow> { - return subjectDAO.getSubjects() + val uiState: StateFlow = subjectDAO.getSubjects() + .map { SubjectUiState.Succes(it) } + .stateIn( + scope = viewModelScope, + initialValue = SubjectUiState.Loading, + started = SharingStarted.Eagerly, + ) + + fun onAddSubject(open: (String) -> Unit) { + open(StudeezDestinations.ADD_SUBJECT_FORM) } fun onViewSubject(subject: Subject, open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt index 2361399..e2adbc1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskViewModel.kt @@ -1,12 +1,10 @@ package be.ugent.sel.studeez.screens.tasks -import android.util.Log 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.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 @@ -17,7 +15,6 @@ import javax.inject.Inject @HiltViewModel class TaskViewModel @Inject constructor( private val taskDAO: TaskDAO, - private val subjectDAO: SubjectDAO, private val selectedSubject: SelectedSubject, private val selectedTask: SelectedTask, logService: LogService, @@ -30,11 +27,6 @@ class TaskViewModel @Inject constructor( return taskDAO.getTasks(selectedSubject()) } - fun deleteSubject(open: (String) -> Unit) { - subjectDAO.deleteSubject(selectedSubject()) - open(StudeezDestinations.SUBJECT_SCREEN) - } - fun getSelectedSubject(): Subject { return selectedSubject() } @@ -44,12 +36,11 @@ class TaskViewModel @Inject constructor( } fun archiveTask(task: Task) { - task.archive() - taskDAO.updateTask(task) + taskDAO.updateTask(task.copy(archived = true)) } fun toggleTaskCompleted(task: Task, completed: Boolean) { - taskDAO.toggleTaskCompleted(task, completed) + taskDAO.updateTask(task.copy(completed = completed)) } fun editSubject(open: (String) -> Unit) { From e9cc4f4a0c9eaae4b6375176ae8a967be6e25284 Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 21:37:26 +0200 Subject: [PATCH 049/163] show studytime --- .../common/composable/tasks/SubjectEntry.kt | 20 +++++++++++++------ .../studeez/screens/tasks/SubjectScreen.kt | 7 +++++++ .../studeez/screens/tasks/SubjectViewModel.kt | 4 ++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt index a6b89bf..5db2af3 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt @@ -9,6 +9,8 @@ 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.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -21,13 +23,17 @@ import androidx.compose.ui.unit.dp 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 +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf import be.ugent.sel.studeez.R.string as AppText @Composable fun SubjectEntry( subject: Subject, onViewSubject: () -> Unit, + getStudyTime: () -> Flow, ) { + val studytime by getStudyTime().collectAsState(initial = 0) Card( modifier = Modifier .fillMaxWidth() @@ -64,7 +70,7 @@ fun SubjectEntry( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = HoursMinutesSeconds(subject.time).toString(), + text = HoursMinutesSeconds(studytime).toString(), ) Row( verticalAlignment = Alignment.CenterVertically, @@ -74,7 +80,7 @@ fun SubjectEntry( imageVector = Icons.Default.List, contentDescription = stringResource(id = AppText.tasks) ) - Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") // TODO + Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") } } } @@ -98,11 +104,12 @@ fun SubjectEntryPreview() { subject = Subject( name = "Test Subject", argb_color = 0xFFFFD200, - time = 60, taskCount = 5, taskCompletedCount = 2, ), - ) {} + onViewSubject = {}, + getStudyTime = { flowOf() } + ) } @Preview @@ -112,7 +119,8 @@ fun OverflowSubjectEntryPreview() { subject = Subject( name = "Testttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", argb_color = 0xFFFFD200, - time = 60, ), - ) {} + onViewSubject = {}, + getStudyTime = { flowOf() } + ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt index 115c75e..7ff5636 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt @@ -19,6 +19,8 @@ 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 @@ -34,6 +36,7 @@ fun SubjectRoute( navigationBarActions = navigationBarActions, onAddSubject = { viewModel.onAddSubject(open) }, onViewSubject = { viewModel.onViewSubject(it, open) }, + getStudyTime = viewModel::getStudyTime, uiState, ) } @@ -44,6 +47,7 @@ fun SubjectScreen( navigationBarActions: NavigationBarActions, onAddSubject: () -> Unit, onViewSubject: (Subject) -> Unit, + getStudyTime: (Subject) -> Flow, uiState: SubjectUiState, ) { PrimaryScreenTemplate( @@ -72,6 +76,7 @@ fun SubjectScreen( SubjectEntry( subject = it, onViewSubject = { onViewSubject(it) }, + getStudyTime = { getStudyTime(it) }, ) } } @@ -89,6 +94,7 @@ fun SubjectScreenPreview() { navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), onAddSubject = {}, onViewSubject = {}, + getStudyTime = { flowOf() }, uiState = SubjectUiState.Succes( listOf( Subject( @@ -109,6 +115,7 @@ fun SubjectScreenLoadingPreview() { navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), onAddSubject = {}, onViewSubject = {}, + getStudyTime = { flowOf() }, uiState = SubjectUiState.Loading ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt index 8587327..f384489 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt @@ -30,6 +30,10 @@ class SubjectViewModel @Inject constructor( open(StudeezDestinations.ADD_SUBJECT_FORM) } + fun getStudyTime(subject: Subject): Flow { + return subjectDAO.getStudyTime(subject) + } + fun onViewSubject(subject: Subject, open: (String) -> Unit) { selectedSubject.set(subject) open(StudeezDestinations.TASKS_SCREEN) From 0e4200057b5fdb2df45c31bdb7b227b44e451fb8 Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 21:56:25 +0200 Subject: [PATCH 050/163] fix selectedSubject --- .../sel/studeez/navigation/StudeezNavGraph.kt | 12 ++--- .../{tasks => subjects}/SubjectScreen.kt | 2 +- .../{tasks => subjects}/SubjectUiState.kt | 2 +- .../{tasks => subjects}/SubjectViewModel.kt | 2 +- .../form}/SubjectFormScreen.kt | 8 +-- .../form}/SubjectFormUiState.kt | 2 +- .../form}/SubjectFormViewModel.kt | 54 ++++++++++++------- .../tasks/{forms => form}/TaskFormScreen.kt | 2 +- .../tasks/{forms => form}/TaskFormUiState.kt | 2 +- .../{forms => form}/TaskFormViewModel.kt | 5 +- 10 files changed, 53 insertions(+), 38 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/{tasks => subjects}/SubjectScreen.kt (98%) rename app/src/main/java/be/ugent/sel/studeez/screens/{tasks => subjects}/SubjectUiState.kt (81%) rename app/src/main/java/be/ugent/sel/studeez/screens/{tasks => subjects}/SubjectViewModel.kt (96%) rename app/src/main/java/be/ugent/sel/studeez/screens/{tasks/forms => subjects/form}/SubjectFormScreen.kt (95%) rename app/src/main/java/be/ugent/sel/studeez/screens/{tasks/forms => subjects/form}/SubjectFormUiState.kt (64%) rename app/src/main/java/be/ugent/sel/studeez/screens/{tasks/forms => subjects/form}/SubjectFormViewModel.kt (67%) rename app/src/main/java/be/ugent/sel/studeez/screens/tasks/{forms => form}/TaskFormScreen.kt (98%) rename app/src/main/java/be/ugent/sel/studeez/screens/tasks/{forms => form}/TaskFormUiState.kt (53%) rename app/src/main/java/be/ugent/sel/studeez/screens/tasks/{forms => form}/TaskFormViewModel.kt (93%) diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index 5becc44..6ef1606 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -24,12 +24,12 @@ import be.ugent.sel.studeez.screens.sessions.SessionsRoute import be.ugent.sel.studeez.screens.settings.SettingsRoute import be.ugent.sel.studeez.screens.sign_up.SignUpRoute import be.ugent.sel.studeez.screens.splash.SplashRoute -import be.ugent.sel.studeez.screens.tasks.SubjectRoute +import be.ugent.sel.studeez.screens.subjects.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.subjects.form.SubjectCreateRoute +import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute +import be.ugent.sel.studeez.screens.tasks.form.TaskAddRoute +import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute import be.ugent.sel.studeez.screens.timer_form.timer_type_select.TimerTypeSelectScreen @@ -81,7 +81,7 @@ fun StudeezNavGraph( } composable(StudeezDestinations.ADD_SUBJECT_FORM) { - SubjectAddRoute( + SubjectCreateRoute( goBack = goBack, openAndPopUp = openAndPopUp, viewModel = hiltViewModel(), diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt similarity index 98% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt index 7ff5636..ab2cff4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks +package be.ugent.sel.studeez.screens.subjects import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectUiState.kt similarity index 81% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectUiState.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectUiState.kt index 38adae4..2e44e27 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectUiState.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks +package be.ugent.sel.studeez.screens.subjects import be.ugent.sel.studeez.data.local.models.task.Subject diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt similarity index 96% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt index f384489..c158529 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/SubjectViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks +package be.ugent.sel.studeez.screens.subjects import androidx.lifecycle.viewModelScope import be.ugent.sel.studeez.data.SelectedSubject diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt similarity index 95% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt index 74bc7d2..19e6816 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks.forms +package be.ugent.sel.studeez.screens.subjects.form import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column @@ -19,10 +19,10 @@ import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.R.string as AppText @Composable -fun SubjectAddRoute( +fun SubjectCreateRoute( goBack: () -> Unit, openAndPopUp: (String, String) -> Unit, - viewModel: SubjectFormViewModel, + viewModel: SubjectCreateFormViewModel, ) { val uiState by viewModel.uiState SubjectForm( @@ -39,7 +39,7 @@ fun SubjectAddRoute( fun SubjectEditRoute( goBack: () -> Unit, openAndPopUp: (String, String) -> Unit, - viewModel: SubjectFormViewModel, + viewModel: SubjectEditFormViewModel, ) { val uiState by viewModel.uiState SubjectForm( diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt similarity index 64% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormUiState.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt index 5418b74..9fdba01 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks.forms +package be.ugent.sel.studeez.screens.subjects.form data class SubjectFormUiState( val name: String = "", diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt similarity index 67% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index 68ebd3e..37a9e5b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -1,5 +1,6 @@ -package be.ugent.sel.studeez.screens.tasks.forms +package be.ugent.sel.studeez.screens.subjects.form +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.data.SelectedSubject import be.ugent.sel.studeez.data.local.models.task.Subject @@ -10,25 +11,17 @@ 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, +abstract class SubjectFormViewModel( + protected val subjectDAO: SubjectDAO, + protected 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 + abstract val uiState: MutableState - private val name: String + protected val name: String get() = uiState.value.name - private val color: Long + protected val color: Long get() = uiState.value.color fun onNameChange(newValue: String) { @@ -38,11 +31,15 @@ class SubjectFormViewModel @Inject constructor( 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) - } +@HiltViewModel +class SubjectCreateFormViewModel @Inject constructor( + subjectDAO: SubjectDAO, + selectedSubject: SelectedSubject, + logService: LogService, +) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) { + override val uiState = mutableStateOf(SubjectFormUiState()) fun onCreate(openAndPopUp: (String, String) -> Unit) { val newSubject = Subject( @@ -57,6 +54,25 @@ class SubjectFormViewModel @Inject constructor( // open(StudeezDestinations.TASKS_SCREEN) openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.ADD_SUBJECT_FORM) } +} + +@HiltViewModel +class SubjectEditFormViewModel @Inject constructor( + subjectDAO: SubjectDAO, + selectedSubject: SelectedSubject, + logService: LogService, +) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) { + override val uiState = mutableStateOf( + SubjectFormUiState( + name = selectedSubject().name, + color = selectedSubject().argb_color + ) + ) + + fun onDelete(openAndPopUp: (String, String) -> Unit) { + subjectDAO.deleteSubject(selectedSubject()) + openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) + } fun onEdit(openAndPopUp: (String, String) -> Unit) { val newSubject = selectedSubject().copy( diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt similarity index 98% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt index 62b6c6c..b09dc66 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks.forms +package be.ugent.sel.studeez.screens.tasks.form import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormUiState.kt similarity index 53% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormUiState.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormUiState.kt index d967d59..6156fb7 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormUiState.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks.forms +package be.ugent.sel.studeez.screens.tasks.form data class TaskFormUiState( val name: String = "", diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormViewModel.kt similarity index 93% rename from app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormViewModel.kt index 03ad32b..8ae91ae 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/forms/TaskFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormViewModel.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.tasks.forms +package be.ugent.sel.studeez.screens.tasks.form import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.data.SelectedSubject @@ -11,8 +11,7 @@ import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject -@HiltViewModel -class TaskFormViewModel @Inject constructor( +abstract class TaskFormViewModel( private val taskDAO: TaskDAO, private val selectedSubject: SelectedSubject, private val selectedTask: SelectedTask, From e333a0f6266982e372203f716eb3d59b146786e1 Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 22:03:38 +0200 Subject: [PATCH 051/163] fix selectedTask --- .../sel/studeez/navigation/StudeezNavGraph.kt | 4 +- .../screens/tasks/form/TaskFormScreen.kt | 6 +-- .../screens/tasks/form/TaskFormViewModel.kt | 46 +++++++++++++------ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index 6ef1606..37085f1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -28,7 +28,7 @@ import be.ugent.sel.studeez.screens.subjects.SubjectRoute import be.ugent.sel.studeez.screens.tasks.TaskRoute import be.ugent.sel.studeez.screens.subjects.form.SubjectCreateRoute import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute -import be.ugent.sel.studeez.screens.tasks.form.TaskAddRoute +import be.ugent.sel.studeez.screens.tasks.form.TaskCreateRoute import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute import be.ugent.sel.studeez.screens.timer_form.TimerEditRoute @@ -105,7 +105,7 @@ fun StudeezNavGraph( } composable(StudeezDestinations.ADD_TASK_FORM) { - TaskAddRoute( + TaskCreateRoute( goBack = goBack, openAndPopUp = openAndPopUp, viewModel = hiltViewModel(), diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt index b09dc66..92302ea 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt @@ -18,10 +18,10 @@ import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.R.string as AppText @Composable -fun TaskAddRoute( +fun TaskCreateRoute( goBack: () -> Unit, openAndPopUp: (String, String) -> Unit, - viewModel: TaskFormViewModel, + viewModel: TaskCreateFormViewModel, ) { val uiState by viewModel.uiState TaskForm( @@ -37,7 +37,7 @@ fun TaskAddRoute( fun TaskEditRoute( goBack: () -> Unit, openAndPopUp: (String, String) -> Unit, - viewModel: TaskFormViewModel, + viewModel: TaskEditFormViewModel, ) { val uiState by viewModel.uiState TaskForm( diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormViewModel.kt index 8ae91ae..07cba5d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormViewModel.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.screens.tasks.form +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.data.SelectedSubject import be.ugent.sel.studeez.data.SelectedTask @@ -12,37 +13,54 @@ import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject abstract class TaskFormViewModel( - private val taskDAO: TaskDAO, - private val selectedSubject: SelectedSubject, - private val selectedTask: SelectedTask, + protected val taskDAO: TaskDAO, + protected val selectedSubject: SelectedSubject, + protected val selectedTask: SelectedTask, logService: LogService, ) : StudeezViewModel(logService) { - var uiState = mutableStateOf( - if (selectedTask.isSet()) TaskFormUiState(selectedTask().name) else TaskFormUiState() - ) - private set + abstract val uiState: MutableState - private val name: String + protected 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) - } +@HiltViewModel +class TaskCreateFormViewModel @Inject constructor( + taskDAO: TaskDAO, + selectedSubject: SelectedSubject, + selectedTask: SelectedTask, + logService: LogService, +) : TaskFormViewModel(taskDAO, selectedSubject, selectedTask, logService) { + override val uiState = mutableStateOf(TaskFormUiState()) 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) } +} + +@HiltViewModel +class TaskEditFormViewModel @Inject constructor( + taskDAO: TaskDAO, + selectedSubject: SelectedSubject, + selectedTask: SelectedTask, + logService: LogService, +) : TaskFormViewModel(taskDAO, selectedSubject, selectedTask, logService) { + override val uiState = mutableStateOf(TaskFormUiState()) + + fun onDelete(openAndPopUp: (String, String) -> Unit) { + taskDAO.deleteTask(selectedTask()) + openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM) + } fun onEdit(openAndPopUp: (String, String) -> Unit) { - val newTask = Task(name = name) + val newTask = selectedTask().copy(name = name) taskDAO.updateTask(newTask) openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_TASK_FORM) } -} \ No newline at end of file +} From d4c017ef1b7780d803748e3d9c64d6ea894b7ca3 Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 22:11:52 +0200 Subject: [PATCH 052/163] add time to task when saving session report --- .../studeez/screens/session_recap/SessionRecapViewModel.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt index 5fb4943..5fdd95c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt @@ -1,9 +1,11 @@ package be.ugent.sel.studeez.screens.session_recap +import be.ugent.sel.studeez.data.SelectedTask import be.ugent.sel.studeez.data.SessionReportState import be.ugent.sel.studeez.data.local.models.SessionReport import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.SessionDAO +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 @@ -13,6 +15,8 @@ import javax.inject.Inject class SessionRecapViewModel @Inject constructor( sessionReportState: SessionReportState, private val sessionDAO: SessionDAO, + private val taskDAO: TaskDAO, + private val selectedTask: SelectedTask, logService: LogService ) : StudeezViewModel(logService) { @@ -24,6 +28,8 @@ class SessionRecapViewModel @Inject constructor( fun saveSession(open: (String, String) -> Unit) { sessionDAO.saveSession(getSessionReport()) + val newTask = selectedTask().copy(time = selectedTask().time + report.studyTime) + taskDAO.updateTask(newTask) open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) } From c3424b99964615b0477995761f1b751e81d0ce3b Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 22:26:11 +0200 Subject: [PATCH 053/163] archive subjects --- .../sel/studeez/data/local/models/task/Subject.kt | 10 ++++++++-- .../domain/implementation/FireBaseSubjectDAO.kt | 15 ++++++++++++--- .../domain/implementation/FireBaseTaskDAO.kt | 10 +++++----- .../screens/subjects/form/SubjectFormViewModel.kt | 2 +- .../ugent/sel/studeez/screens/tasks/TaskScreen.kt | 3 --- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt index 6f29e22..74ebe9f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt @@ -7,10 +7,16 @@ data class Subject( @DocumentId val id: String = "", val name: String = "", val argb_color: Long = 0, + var archived: Boolean = false, @get:Exclude @set:Exclude var taskCount: Int = 0, @get:Exclude @set:Exclude var taskCompletedCount: Int = 0, - @get:Exclude @set:Exclude - var time: Int = 0, ) + +object SubjectDocument { + const val id = "id" + const val name = "name" + const val archived = "archived" + const val argb_color = "argb_color" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt index 3e9ae13..b023986 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt @@ -1,12 +1,14 @@ 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.SubjectDocument import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.SubjectDAO import be.ugent.sel.studeez.domain.TaskDAO import com.google.firebase.firestore.AggregateSource import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.Query import com.google.firebase.firestore.ktx.snapshots import com.google.firebase.firestore.ktx.toObject import kotlinx.coroutines.flow.Flow @@ -21,6 +23,7 @@ class FireBaseSubjectDAO @Inject constructor( ) : SubjectDAO { override fun getSubjects(): Flow> { return currentUserSubjectsCollection() + .subjectNotArchived() .snapshots() .map { it.toObjects(Subject::class.java) } .map { subjects -> @@ -50,7 +53,7 @@ class FireBaseSubjectDAO @Inject constructor( override suspend fun getTaskCount(subject: Subject): Int { return subjectTasksCollection(subject) - .nonArchived() + .taskNotArchived() .count() .get(AggregateSource.SERVER) .await() @@ -59,8 +62,8 @@ class FireBaseSubjectDAO @Inject constructor( override suspend fun getCompletedTaskCount(subject: Subject): Int { return subjectTasksCollection(subject) - .nonArchived() - .completed() + .taskNotArchived() + .taskNotCompleted() .count() .get(AggregateSource.SERVER) .await() @@ -83,4 +86,10 @@ class FireBaseSubjectDAO @Inject constructor( .collection(FireBaseCollections.SUBJECT_COLLECTION) .document(subject.id) .collection(FireBaseCollections.TASK_COLLECTION) + + fun CollectionReference.subjectNotArchived(): Query = + this.whereEqualTo(SubjectDocument.archived, false) + + fun Query.subjectNotArchived(): Query = + this.whereEqualTo(SubjectDocument.archived, false) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt index bde5c6f..685b237 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt @@ -21,7 +21,7 @@ class FireBaseTaskDAO @Inject constructor( ) : TaskDAO { override fun getTasks(subject: Subject): Flow> { return selectedSubjectTasksCollection(subject.id) - .nonArchived() + .taskNotArchived() .snapshots() .map { it.toObjects(Task::class.java) } } @@ -55,14 +55,14 @@ class FireBaseTaskDAO @Inject constructor( // Extend CollectionReference and Query with some filters -fun CollectionReference.nonArchived(): Query = +fun CollectionReference.taskNotArchived(): Query = this.whereEqualTo(TaskDocument.archived, false) -fun Query.nonArchived(): Query = +fun Query.taskNotArchived(): Query = this.whereEqualTo(TaskDocument.archived, false) -fun CollectionReference.completed(): Query = +fun CollectionReference.taskNotCompleted(): Query = this.whereEqualTo(TaskDocument.completed, true) -fun Query.completed(): Query = +fun Query.taskNotCompleted(): Query = this.whereEqualTo(TaskDocument.completed, true) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index 37a9e5b..533123b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -70,7 +70,7 @@ class SubjectEditFormViewModel @Inject constructor( ) fun onDelete(openAndPopUp: (String, String) -> Unit) { - subjectDAO.deleteSubject(selectedSubject()) + subjectDAO.updateSubject(selectedSubject().copy(archived = true)) openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt index 1b56bc6..7760d84 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt @@ -27,7 +27,6 @@ data class TaskActions( val addTask: () -> Unit, val getSubject: () -> Subject, val getTasks: () -> Flow>, - val deleteTask: (Task) -> Unit, val onCheckTask: (Task, Boolean) -> Unit, val editSubject: () -> Unit, val startTask: (Task) -> Unit, @@ -39,7 +38,6 @@ fun getTaskActions(viewModel: TaskViewModel, open: (String) -> Unit): TaskAction addTask = { viewModel.addTask(open) }, getTasks = viewModel::getTasks, getSubject = viewModel::getSelectedSubject, - deleteTask = viewModel::deleteTask, onCheckTask = { task, isChecked -> viewModel.toggleTaskCompleted(task, isChecked) }, editSubject = { viewModel.editSubject(open) }, startTask = { task -> viewModel.startTask(task, open) }, @@ -110,7 +108,6 @@ fun TaskScreenPreview() { {}, { Subject(name = "Test Subject") }, { flowOf() }, - {}, { _, _ -> run {} }, {}, {}, From 6a560fee795e95cf7d726454849f91e7dda31d61 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Tue, 9 May 2023 22:29:07 +0200 Subject: [PATCH 054/163] button on feed not visible when task is checked --- .../common/composable/ButtonComposable.kt | 3 ++- .../common/composable/feed/FeedEntry.kt | 25 +++++++++++++------ .../studeez/data/local/models/FeedEntry.kt | 3 ++- .../domain/implementation/FirebaseFeedDAO.kt | 3 ++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt index 73ae1b5..0ba2ffe 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt @@ -20,6 +20,7 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -84,7 +85,7 @@ fun StealthButton( backgroundColor = MaterialTheme.colors.surface, contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f) ), - border = BorderStroke(3.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)) + border = BorderStroke(2.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)) ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt index 6dce710..76c9075 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt @@ -4,7 +4,10 @@ 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.Icon import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Info import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -16,6 +19,7 @@ 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.common.ext.fieldModifier import be.ugent.sel.studeez.data.local.models.FeedEntry import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds @@ -69,13 +73,20 @@ fun FeedEntry( Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) } } - StealthButton( - text = R.string.continue_task, - modifier = Modifier - .padding(start = 10.dp, end = 5.dp) - .weight(6f) - ) { - continueWithTask() + if (!feedEntry.isArchived) { + StealthButton( + text = R.string.continue_task, + modifier = Modifier + .padding(start = 10.dp, end = 5.dp) + .weight(6f) + ) { + continueWithTask() + } + } else { + Row { + Icon(imageVector = Icons.Default.Info, contentDescription = null) + Text(text = "Deleted", modifier = Modifier.padding(horizontal = 5.dp)) + } } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt index e24cd24..8733c48 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/FeedEntry.kt @@ -9,5 +9,6 @@ data class FeedEntry( val taskId: String = "", // Name of task is not unique val subjectId: String = "", val totalStudyTime: Int = 0, - val endTime: Timestamp = Timestamp(0, 0) + val endTime: Timestamp = Timestamp(0, 0), + val isArchived: Boolean = false ) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index c116fbf..88142fb 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -72,7 +72,8 @@ class FirebaseFeedDAO @Inject constructor( taskId = task.id, subjectId = subject.id, totalStudyTime = sessionReport.studyTime, - endTime = sessionReport.endTime + endTime = sessionReport.endTime, + isArchived = task.completed ) } } \ No newline at end of file From a5b98d6bf824110efe31f7d59348da733bcda76b Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 22:38:23 +0200 Subject: [PATCH 055/163] move new-task button to top --- .../be/ugent/sel/studeez/data/local/models/task/Task.kt | 6 +----- .../java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt index e42ce5d..ff2748d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Task.kt @@ -9,11 +9,7 @@ data class Task( val time: Int = 0, val subjectId: String = "", var archived: Boolean = false, -) { - fun archive() { - this.archived = true - } -} +) object TaskDocument { const val id = "id" diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt index 7760d84..acdb64b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt @@ -71,6 +71,7 @@ fun TaskScreen( Column( modifier = Modifier.padding(top = 5.dp) ) { + NewTaskSubjectButton(onClick = taskActions.addTask, AppText.new_task) LazyColumn { items(tasks.value) { TaskEntry( @@ -81,7 +82,6 @@ fun TaskScreen( ) } } - NewTaskSubjectButton(onClick = taskActions.addTask, AppText.new_task) } } } From 7706c2d070af9a9a8e7f24fc29fa0c9afaf2f5d0 Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 22:44:08 +0200 Subject: [PATCH 056/163] #22 sort tasks by completed --- .../be/ugent/sel/studeez/screens/tasks/TaskScreen.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt index acdb64b..516b836 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/TaskScreen.kt @@ -73,7 +73,15 @@ fun TaskScreen( ) { NewTaskSubjectButton(onClick = taskActions.addTask, AppText.new_task) LazyColumn { - items(tasks.value) { + items(tasks.value.filter { !it.completed }) { + TaskEntry( + task = it, + onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, + onArchiveTask = { taskActions.archiveTask(it) }, + onStartTask = { taskActions.startTask(it) } + ) + } + items(tasks.value.filter { it.completed }) { TaskEntry( task = it, onCheckTask = { isChecked -> taskActions.onCheckTask(it, isChecked) }, From 875732fd2dd0604917742e496720c4619f9f888f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Tue, 9 May 2023 23:12:52 +0200 Subject: [PATCH 057/163] changed stealthbutton to blue + disbale feed continue when task or subject is archived --- .../common/composable/ButtonComposable.kt | 12 +++++++-- .../common/composable/feed/FeedEntry.kt | 27 ++++++++----------- .../sel/studeez/common/ext/ModifierExt.kt | 11 ++++++++ .../domain/implementation/FirebaseFeedDAO.kt | 2 +- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt index 0ba2ffe..c96994d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt @@ -2,6 +2,7 @@ package be.ugent.sel.studeez.common.composable import androidx.annotation.StringRes import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -49,6 +50,7 @@ fun BasicButton( modifier: Modifier = Modifier, colors: ButtonColors = ButtonDefaults.buttonColors(), border: BorderStroke? = null, + enabled: Boolean = true, onClick: () -> Unit, ) { Button( @@ -57,6 +59,7 @@ fun BasicButton( shape = defaultButtonShape(), colors = colors, border = border, + enabled = enabled, ) { Text( text = stringResource(text), @@ -75,17 +78,22 @@ fun BasicButtonPreview() { fun StealthButton( @StringRes text: Int, modifier: Modifier = Modifier.card(), + enabled: Boolean = true, onClick: () -> Unit, ) { + //val clickablemodifier = if (disabled) Modifier.clickable(indication = null) else modifier + val borderColor = if (enabled) MaterialTheme.colors.primary + else MaterialTheme.colors.onSurface.copy(alpha = 0.3f) BasicButton( text = text, onClick = onClick, modifier = modifier, + enabled = enabled, colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.surface, - contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f) + contentColor = borderColor ), - border = BorderStroke(2.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)) + border = BorderStroke(2.dp, borderColor) ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt index 76c9075..179a9c0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt @@ -4,10 +4,7 @@ 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.Icon import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Info import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -19,9 +16,9 @@ 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.common.ext.fieldModifier 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 FeedEntry( @@ -73,21 +70,19 @@ fun FeedEntry( Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) } } - if (!feedEntry.isArchived) { - StealthButton( - text = R.string.continue_task, - modifier = Modifier - .padding(start = 10.dp, end = 5.dp) - .weight(6f) - ) { + val buttonText: Int = if (feedEntry.isArchived) AppText.deleted else AppText.continue_task + StealthButton( + text = buttonText, + enabled = !feedEntry.isArchived, + modifier = Modifier + .padding(start = 10.dp, end = 5.dp) + .weight(6f) + ) { + if (!feedEntry.isArchived) { continueWithTask() } - } else { - Row { - Icon(imageVector = Icons.Default.Info, contentDescription = null) - Text(text = "Deleted", modifier = Modifier.padding(horizontal = 5.dp)) - } } + } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt b/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt index 7280ab3..8c96232 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt @@ -1,14 +1,25 @@ package be.ugent.sel.studeez.common.ext +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.composed import androidx.compose.ui.unit.dp fun Modifier.textButton(): Modifier { return this.fillMaxWidth().padding(16.dp, 8.dp, 16.dp, 0.dp) } +fun Modifier.noRippleClickable(onClick: () -> Unit): Modifier = composed { + clickable(indication = null, + interactionSource = remember { MutableInteractionSource() }) { + onClick() + } +} + fun Modifier.basicButton(): Modifier { return this.fillMaxWidth().padding(16.dp, 8.dp) } diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt index b46b9d0..6c445bf 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFeedDAO.kt @@ -75,7 +75,7 @@ class FirebaseFeedDAO @Inject constructor( subjectId = subject.id, totalStudyTime = sessionReport.studyTime, endTime = sessionReport.endTime, - isArchived = task.completed + isArchived = task.archived || subject.archived ) } } \ No newline at end of file From 43ccab6d083812a674fe3898dc372aad6212812b Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 23:20:24 +0200 Subject: [PATCH 058/163] fix missing rescource string --- app/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d58be52..e5ed407 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,7 @@ Continue + Deleted This is your feed Create you first subject and tasks to get started From 08b86c15dd12861a8dfa602daa863c8c10ae85e5 Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 23:23:11 +0200 Subject: [PATCH 059/163] fix hint not very clear --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e5ed407..70c4558 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,7 +33,7 @@ Continue Deleted This is your feed - Create you first subject and tasks to get started + Click here to create you first subject and tasks to get started Tasks From 25ed3dfdd22a783f39463db3820cde5cb7c5a1d0 Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 23:25:39 +0200 Subject: [PATCH 060/163] add feedentry overflow preview -> needs fixing --- .../common/composable/feed/FeedEntry.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt index 179a9c0..ff950d6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/feed/FeedEntry.kt @@ -14,7 +14,6 @@ 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 @@ -70,7 +69,8 @@ fun FeedEntry( Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) } } - val buttonText: Int = if (feedEntry.isArchived) AppText.deleted else AppText.continue_task + val buttonText: Int = + if (feedEntry.isArchived) AppText.deleted else AppText.continue_task StealthButton( text = buttonText, enabled = !feedEntry.isArchived, @@ -90,6 +90,20 @@ fun FeedEntry( @Preview @Composable fun FeedEntryPreview() { + FeedEntry( + continueWithTask = {}, + feedEntry = FeedEntry( + argb_color = 0xFFFFD200, + subJectName = "Test Subject", + taskName = "Test Task", + totalStudyTime = 20, + ) + ) +} + +@Preview +@Composable +fun FeedEntryOverflowPreview() { FeedEntry( continueWithTask = {}, feedEntry = FeedEntry( From 3c1bc9bb1b6024b770636a7c573e826f3261fc1b Mon Sep 17 00:00:00 2001 From: brreynie Date: Tue, 9 May 2023 23:51:05 +0200 Subject: [PATCH 061/163] refactored all states to communicate between viewmodels --- .../ugent/sel/studeez/data/EditTimerState.kt | 11 ----- .../ugent/sel/studeez/data/SelectedState.kt | 45 +++++++++++++++++++ .../ugent/sel/studeez/data/SelectedSubject.kt | 20 --------- .../be/ugent/sel/studeez/data/SelectedTask.kt | 21 --------- .../sel/studeez/data/SelectedTimerState.kt | 14 ------ .../sel/studeez/data/SessionReportState.kt | 14 ------ .../screens/session/SessionViewModel.kt | 20 ++++----- .../session_recap/SessionRecapViewModel.kt | 11 +++-- .../screens/timer_form/TimerFormViewModel.kt | 9 ++-- .../TimerTypeSelectViewModel.kt | 6 +-- .../timer_overview/TimerOverviewViewModel.kt | 12 ++--- .../TimerSelectionViewModel.kt | 10 ++--- .../InvisibleSessionManagerTest.kt | 10 ++--- 13 files changed, 78 insertions(+), 125 deletions(-) delete mode 100644 app/src/main/java/be/ugent/sel/studeez/data/EditTimerState.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/data/SelectedTimerState.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/data/SessionReportState.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/EditTimerState.kt b/app/src/main/java/be/ugent/sel/studeez/data/EditTimerState.kt deleted file mode 100644 index dceec8c..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/data/EditTimerState.kt +++ /dev/null @@ -1,11 +0,0 @@ -package be.ugent.sel.studeez.data - -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer -import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class EditTimerState @Inject constructor(){ - lateinit var timerInfo: TimerInfo -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt new file mode 100644 index 0000000..c52939f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/SelectedState.kt @@ -0,0 +1,45 @@ +package be.ugent.sel.studeez.data + +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.data.local.models.timer_functional.FunctionalTimer +import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Used to cummunicate between viewmodels. + */ +abstract class SelectedState { + abstract var value: T + operator fun invoke() = value + fun set(newValue: T) { + this.value = newValue + } +} + +@Singleton +class SelectedSessionReport @Inject constructor() : SelectedState() { + override lateinit var value: SessionReport +} + +@Singleton +class SelectedTask @Inject constructor() : SelectedState() { + override lateinit var value: Task +} + +@Singleton +class SelectedTimer @Inject constructor() : SelectedState() { + override lateinit var value: FunctionalTimer +} + +@Singleton +class SelectedSubject @Inject constructor() : SelectedState() { + override lateinit var value: Subject +} + +@Singleton +class SelectedTimerInfo @Inject constructor() : SelectedState() { + override lateinit var value: TimerInfo +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt deleted file mode 100644 index fbc7e48..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/data/SelectedSubject.kt +++ /dev/null @@ -1,20 +0,0 @@ -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 -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt deleted file mode 100644 index 9c3f042..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/data/SelectedTask.kt +++ /dev/null @@ -1,21 +0,0 @@ -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 -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SelectedTimerState.kt b/app/src/main/java/be/ugent/sel/studeez/data/SelectedTimerState.kt deleted file mode 100644 index f8fcebd..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/data/SelectedTimerState.kt +++ /dev/null @@ -1,14 +0,0 @@ -package be.ugent.sel.studeez.data - -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer -import javax.inject.Inject -import javax.inject.Singleton - -/** - * Used to communicate the SelectedTimer from the selection screen to the session screen. - * Because this is a singleton-class the view-models of both screens observe the same data. - */ -@Singleton -class SelectedTimerState @Inject constructor(){ - var selectedTimer: FunctionalTimer? = null -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/SessionReportState.kt b/app/src/main/java/be/ugent/sel/studeez/data/SessionReportState.kt deleted file mode 100644 index 47770d0..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/data/SessionReportState.kt +++ /dev/null @@ -1,14 +0,0 @@ -package be.ugent.sel.studeez.data - -import be.ugent.sel.studeez.data.local.models.SessionReport -import javax.inject.Inject -import javax.inject.Singleton - -/** - * Used to communicate the SelectedTimer from the selection screen to the session screen. - * Because this is a singleton-class the view-models of both screens observe the same data. - */ -@Singleton -class SessionReportState @Inject constructor(){ - var sessionReport: SessionReport? = null -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt index cd4c93a..0be4147 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt @@ -1,9 +1,8 @@ package be.ugent.sel.studeez.screens.session +import be.ugent.sel.studeez.data.SelectedSessionReport import be.ugent.sel.studeez.data.SelectedTask -import be.ugent.sel.studeez.data.SelectedTimerState -import be.ugent.sel.studeez.data.SessionReportState -import be.ugent.sel.studeez.data.local.models.task.Task +import be.ugent.sel.studeez.data.SelectedTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.navigation.StudeezDestinations @@ -13,24 +12,21 @@ import javax.inject.Inject @HiltViewModel class SessionViewModel @Inject constructor( - private val selectedTimerState: SelectedTimerState, - private val sessionReportState: SessionReportState, + private val selectedTimer: SelectedTimer, + private val sessionReport: SelectedSessionReport, private val selectedTask: SelectedTask, logService: LogService ) : StudeezViewModel(logService) { - - private val task : Task = selectedTask() - - fun getTimer() : FunctionalTimer { - return selectedTimerState.selectedTimer!! + fun getTimer(): FunctionalTimer { + return selectedTimer() } fun getTask(): String { - return task.name + return selectedTask().name } fun endSession(openAndPopUp: (String, String) -> Unit) { - sessionReportState.sessionReport = getTimer().getSessionReport(task.subjectId, task.id) + sessionReport.set(getTimer().getSessionReport(selectedTask().subjectId, selectedTask().id)) openAndPopUp(StudeezDestinations.SESSION_RECAP, StudeezDestinations.SESSION_SCREEN) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt index 5fdd95c..c11e28f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt @@ -1,7 +1,7 @@ package be.ugent.sel.studeez.screens.session_recap +import be.ugent.sel.studeez.data.SelectedSessionReport import be.ugent.sel.studeez.data.SelectedTask -import be.ugent.sel.studeez.data.SessionReportState import be.ugent.sel.studeez.data.local.models.SessionReport import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.SessionDAO @@ -13,22 +13,21 @@ import javax.inject.Inject @HiltViewModel class SessionRecapViewModel @Inject constructor( - sessionReportState: SessionReportState, + private val selectedSessionReport: SelectedSessionReport, private val sessionDAO: SessionDAO, private val taskDAO: TaskDAO, private val selectedTask: SelectedTask, logService: LogService ) : StudeezViewModel(logService) { - private val report: SessionReport = sessionReportState.sessionReport!! - fun getSessionReport(): SessionReport { - return report + return selectedSessionReport() } fun saveSession(open: (String, String) -> Unit) { sessionDAO.saveSession(getSessionReport()) - val newTask = selectedTask().copy(time = selectedTask().time + report.studyTime) + val newTask = + selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime) taskDAO.updateTask(newTask) open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt index 4c2079c..8a0a4d4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt @@ -1,6 +1,6 @@ package be.ugent.sel.studeez.screens.timer_form -import be.ugent.sel.studeez.data.EditTimerState +import be.ugent.sel.studeez.data.SelectedTimerInfo import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.TimerDAO @@ -10,15 +10,12 @@ import javax.inject.Inject @HiltViewModel class TimerFormViewModel @Inject constructor( - private val editTimerState: EditTimerState, + private val selectedTimerInfo: SelectedTimerInfo, private val timerDAO: TimerDAO, logService: LogService ) : StudeezViewModel(logService) { - - private val timerInfo: TimerInfo = editTimerState.timerInfo - fun getTimerInfo(): TimerInfo { - return timerInfo + return selectedTimerInfo() } fun editTimer(timerInfo: TimerInfo, goBack: () -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectViewModel.kt index 569a36c..c3ed2c4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectViewModel.kt @@ -1,6 +1,6 @@ package be.ugent.sel.studeez.screens.timer_form.timer_type_select -import be.ugent.sel.studeez.data.EditTimerState +import be.ugent.sel.studeez.data.SelectedTimerInfo import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.navigation.StudeezDestinations @@ -10,13 +10,13 @@ import javax.inject.Inject @HiltViewModel class TimerTypeSelectViewModel @Inject constructor( - private val editTimerState: EditTimerState, + private val selectedTimerInfo: SelectedTimerInfo, logService: LogService ) : StudeezViewModel(logService) { fun onTimerTypeChosen(timerInfo: TimerInfo, open: (String) -> Unit) { - editTimerState.timerInfo = timerInfo + selectedTimerInfo.set(timerInfo) open(StudeezDestinations.ADD_TIMER_SCREEN) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewViewModel.kt index 3e10053..395a155 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewViewModel.kt @@ -1,6 +1,6 @@ package be.ugent.sel.studeez.screens.timer_overview -import be.ugent.sel.studeez.data.EditTimerState +import be.ugent.sel.studeez.data.SelectedTimerInfo import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.domain.ConfigurationService import be.ugent.sel.studeez.domain.LogService @@ -15,11 +15,11 @@ import javax.inject.Inject class TimerOverviewViewModel @Inject constructor( private val configurationService: ConfigurationService, private val timerDAO: TimerDAO, - private val editTimerState: EditTimerState, + private val selectedTimerInfo: SelectedTimerInfo, logService: LogService ) : StudeezViewModel(logService) { - fun getUserTimers() : Flow> { + fun getUserTimers(): Flow> { return timerDAO.getUserTimers() } @@ -27,8 +27,8 @@ class TimerOverviewViewModel @Inject constructor( return configurationService.getDefaultTimers() } - fun update(timerInfo: TimerInfo, open: (String) -> Unit) { - editTimerState.timerInfo = timerInfo + fun update(timerInfo: TimerInfo, open: (String) -> Unit) { + selectedTimerInfo.set(timerInfo) open(StudeezDestinations.TIMER_EDIT_SCREEN) } @@ -36,7 +36,7 @@ class TimerOverviewViewModel @Inject constructor( open(StudeezDestinations.TIMER_TYPE_CHOOSING_SCREEN) } - fun delete(timerInfo: TimerInfo) =timerDAO.deleteTimer(timerInfo) + fun delete(timerInfo: TimerInfo) = timerDAO.deleteTimer(timerInfo) fun save(timerInfo: TimerInfo) = timerDAO.saveTimer(timerInfo) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt index a4f646d..c6c6793 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionViewModel.kt @@ -1,10 +1,8 @@ package be.ugent.sel.studeez.screens.timer_selection import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import be.ugent.sel.studeez.data.SelectedTimerState +import be.ugent.sel.studeez.data.SelectedTimer 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.domain.LogService @@ -18,7 +16,7 @@ import javax.inject.Inject @HiltViewModel class TimerSelectionViewModel @Inject constructor( private val timerDAO: TimerDAO, - private val selectedTimerState: SelectedTimerState, + private val selectedTimer: SelectedTimer, logService: LogService ) : StudeezViewModel(logService) { @@ -26,12 +24,12 @@ class TimerSelectionViewModel @Inject constructor( HoursMinutesSeconds(1, 0, 0).getTotalSeconds() ) - fun getAllTimers() : Flow> { + fun getAllTimers(): Flow> { return timerDAO.getAllTimers() } fun startSession(open: (String) -> Unit, timerInfo: TimerInfo) { - selectedTimerState.selectedTimer = timerInfo.getFunctionalTimer() + selectedTimer.set(timerInfo.getFunctionalTimer()) open(StudeezDestinations.SESSION_SCREEN) } } \ No newline at end of file diff --git a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt index f9e34c3..12b6799 100644 --- a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt +++ b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt @@ -1,8 +1,6 @@ package be.ugent.sel.studeez.timer_functional import android.media.MediaPlayer -import be.ugent.sel.studeez.data.SelectedTimerState -import be.ugent.sel.studeez.data.SessionReportState import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer @@ -18,14 +16,14 @@ import org.mockito.kotlin.mock @ExperimentalCoroutinesApi class InvisibleSessionManagerTest { - private var timerState: SelectedTimerState = SelectedTimerState() + private var timerState: SelectedTimer = SelectedTimer() private lateinit var viewModel: SessionViewModel private var mediaPlayer: MediaPlayer = mock() @Test fun InvisibleEndlessTimerTest() = runTest { timerState.selectedTimer = FunctionalEndlessTimer() - viewModel = SessionViewModel(timerState, SessionReportState(), mock()) + viewModel = SessionViewModel(timerState, SessionReport(), mock()) InvisibleSessionManager.setParameters(viewModel, mediaPlayer) val test = launch { @@ -47,7 +45,7 @@ class InvisibleSessionManagerTest { val breakTime = 5 val repeats = 1 timerState.selectedTimer = FunctionalPomodoroTimer(studyTime, breakTime, repeats) - viewModel = SessionViewModel(timerState, SessionReportState(), mock()) + viewModel = SessionViewModel(timerState, SessionReport(), mock()) InvisibleSessionManager.setParameters(viewModel, mediaPlayer) val test = launch { @@ -80,7 +78,7 @@ class InvisibleSessionManagerTest { @Test fun InvisibleCustomTimerTest() = runTest { timerState.selectedTimer = FunctionalCustomTimer(5) - viewModel = SessionViewModel(timerState, SessionReportState(), mock()) + viewModel = SessionViewModel(timerState, SessionReport(), mock()) InvisibleSessionManager.setParameters(viewModel, mediaPlayer) val test = launch { From efd92ce832d23d7bd18eb7fcf3119758a9f8a039 Mon Sep 17 00:00:00 2001 From: brreynie Date: Wed, 10 May 2023 09:59:17 +0200 Subject: [PATCH 062/163] fix tests unable to compile --- .../InvisibleSessionManagerTest.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt index 12b6799..54f673d 100644 --- a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt +++ b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt @@ -1,9 +1,13 @@ package be.ugent.sel.studeez.timer_functional import android.media.MediaPlayer +import be.ugent.sel.studeez.data.SelectedSessionReport +import be.ugent.sel.studeez.data.SelectedTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.implementation.LogServiceImpl import be.ugent.sel.studeez.screens.session.InvisibleSessionManager import be.ugent.sel.studeez.screens.session.SessionViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -16,14 +20,14 @@ import org.mockito.kotlin.mock @ExperimentalCoroutinesApi class InvisibleSessionManagerTest { - private var timerState: SelectedTimer = SelectedTimer() + private var selectedTimer: SelectedTimer = SelectedTimer() private lateinit var viewModel: SessionViewModel private var mediaPlayer: MediaPlayer = mock() @Test fun InvisibleEndlessTimerTest() = runTest { - timerState.selectedTimer = FunctionalEndlessTimer() - viewModel = SessionViewModel(timerState, SessionReport(), mock()) + selectedTimer.set(FunctionalEndlessTimer()) + viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) InvisibleSessionManager.setParameters(viewModel, mediaPlayer) val test = launch { @@ -44,8 +48,8 @@ class InvisibleSessionManagerTest { val studyTime = 10 val breakTime = 5 val repeats = 1 - timerState.selectedTimer = FunctionalPomodoroTimer(studyTime, breakTime, repeats) - viewModel = SessionViewModel(timerState, SessionReport(), mock()) + selectedTimer.set(FunctionalPomodoroTimer(studyTime, breakTime, repeats)) + viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) InvisibleSessionManager.setParameters(viewModel, mediaPlayer) val test = launch { @@ -77,8 +81,8 @@ class InvisibleSessionManagerTest { @Test fun InvisibleCustomTimerTest() = runTest { - timerState.selectedTimer = FunctionalCustomTimer(5) - viewModel = SessionViewModel(timerState, SessionReport(), mock()) + selectedTimer.set(FunctionalCustomTimer(5)) + viewModel = SessionViewModel(selectedTimer, SelectedSessionReport(), mock(), LogServiceImpl()) InvisibleSessionManager.setParameters(viewModel, mediaPlayer) val test = launch { From 59044658e6e3b3a60c4c9ed71732bd9875760d68 Mon Sep 17 00:00:00 2001 From: brreynie Date: Wed, 10 May 2023 10:27:08 +0200 Subject: [PATCH 063/163] Update app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt Co-authored-by: lbarraga --- .../java/be/ugent/sel/studeez/common/ext/ModifierExt.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt b/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt index 8c96232..66ade69 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/ext/ModifierExt.kt @@ -13,13 +13,6 @@ fun Modifier.textButton(): Modifier { return this.fillMaxWidth().padding(16.dp, 8.dp, 16.dp, 0.dp) } -fun Modifier.noRippleClickable(onClick: () -> Unit): Modifier = composed { - clickable(indication = null, - interactionSource = remember { MutableInteractionSource() }) { - onClick() - } -} - fun Modifier.basicButton(): Modifier { return this.fillMaxWidth().padding(16.dp, 8.dp) } From d6b2c17f29148636aaa2cef4c8e98a4ce13ceb48 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:31:11 +0200 Subject: [PATCH 064/163] bug fix: singleline was by default false --- .../ugent/sel/studeez/common/composable/TextFieldComposable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt index aadcee3..e3c54ef 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt @@ -47,7 +47,7 @@ fun LabelledInputField( value: String, onNewValue: (String) -> Unit, @StringRes label: Int, - singleLine: Boolean = false + singleLine: Boolean = true ) { OutlinedTextField( value = value, From 9bc64be1ee6657fab3054ca67e1f9463ac3e4950 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:32:08 +0200 Subject: [PATCH 065/163] #110 added extra button in parameters of formscreen --- .../screens/timer_form/TimerFormRoute.kt | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt index 0323dc2..7cef001 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.screens.timer_form import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import be.ugent.sel.studeez.common.composable.DeleteButton import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.R.string as AppText @@ -12,8 +13,16 @@ fun TimerAddRoute( popUp: () -> Unit, viewModel: TimerFormViewModel ) { - TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.add_timer) { - viewModel.saveTimer(it, goBack = popUp) + + + TimerFormScreen( + popUp = popUp, + getTimerInfo = viewModel::getTimerInfo, + extraButton= { }, + AppText.add_timer + ) { + viewModel.saveTimer(it, goBack = {popUp(); popUp()}) + } } @@ -22,7 +31,20 @@ fun TimerEditRoute( popUp: () -> Unit, viewModel: TimerFormViewModel ) { - TimerFormScreen(popUp = popUp, getTimerInfo = viewModel::getTimerInfo, AppText.edit_timer) { + + @Composable + fun deleteButton() { + DeleteButton(text = AppText.delete_subject) { + viewModel.deleteTimer(viewModel.getTimerInfo(), popUp) + } + } + + TimerFormScreen( + popUp = popUp, + getTimerInfo = viewModel::getTimerInfo, + extraButton= { deleteButton() }, + AppText.edit_timer + ) { viewModel.editTimer(it, goBack = popUp) } } @@ -31,12 +53,13 @@ fun TimerEditRoute( fun TimerFormScreen( popUp: () -> Unit, getTimerInfo: () -> TimerInfo, + extraButton: @Composable () -> Unit, @StringRes label: Int, onConfirmClick: (TimerInfo) -> Unit ) { val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen()) SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) { - timerFormScreen(onConfirmClick) + timerFormScreen(onConfirmClick, extraButton) } } From d9294155824a5e31692bdf11a1780f3441198da1 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:32:36 +0200 Subject: [PATCH 066/163] #110 added delete timer function in viewmodel --- .../sel/studeez/screens/timer_form/TimerFormViewModel.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt index 8a0a4d4..c34cd06 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormViewModel.kt @@ -23,6 +23,11 @@ class TimerFormViewModel @Inject constructor( goBack() } + fun deleteTimer(timerInfo: TimerInfo, goBack: () -> Unit) { + timerDAO.deleteTimer(timerInfo) + goBack() + } + fun saveTimer(timerInfo: TimerInfo, goBack: () -> Unit) { timerDAO.saveTimer(timerInfo) goBack() From 88ebbe4de1c6119beb7658cac036db28036ba294 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:33:15 +0200 Subject: [PATCH 067/163] #110 extrabutton added (used for delete) --- .../form_screens/AbstractTimerFormScreen.kt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt index 5f4a17b..168a2f8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt @@ -23,7 +23,10 @@ import be.ugent.sel.studeez.R.string as AppText abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { @Composable - operator fun invoke(onSaveClick: (TimerInfo) -> Unit) { + operator fun invoke( + onSaveClick: (TimerInfo) -> Unit, + extraButton: @Composable () -> Unit + ) { var name by remember { mutableStateOf(timerInfo.name) } var description by remember { mutableStateOf(timerInfo.description) } @@ -34,7 +37,9 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { Column( verticalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxHeight().verticalScroll(rememberScrollState()), + modifier = Modifier + .fillMaxHeight() + .verticalScroll(rememberScrollState()), ) { Column( modifier = Modifier.fillMaxWidth(), @@ -45,7 +50,7 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { LabelledInputField( value = name, onNewValue = { name = it }, - label = R.string.name + label = R.string.name, ) LabelledInputField( @@ -58,8 +63,12 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { ExtraFields() } - BasicButton(R.string.save, Modifier.basicButton()) { - onSaveClick(timerInfo) + + Column { + BasicButton(R.string.save, Modifier.basicButton()) { + onSaveClick(timerInfo) + } + extraButton() } } } From 74d29205624303dafd53e44a30a7c4a39c899391 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 11:33:34 +0200 Subject: [PATCH 068/163] fix previews for extra button --- .../screens/timer_form/form_screens/BreakTimerFormScreen.kt | 2 +- .../screens/timer_form/form_screens/CustomTimerFormScreen.kt | 2 +- .../screens/timer_form/form_screens/EndlessTimerFormScreen.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt index 12d07a4..21e862d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt @@ -50,6 +50,6 @@ fun BreakEditScreenPreview() { 5 ) StudeezTheme { - BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}) + BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}, {}) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt index 27c0657..b0000a0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt @@ -29,6 +29,6 @@ class CustomTimerFormScreen( fun CustomEditScreenPreview() { val customTimerInfo = CustomTimerInfo("custom", "my description", 25) StudeezTheme { - CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}) + CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}, extraButton = {}) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt index 9009fff..e096946 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt @@ -18,6 +18,6 @@ fun EndlessEditScreenPreview() { "My endless timer description", ) StudeezTheme { - EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}) + EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}, {}) } } \ No newline at end of file From 79ed70bb175bd8fdbc037a83c7c537922fcd7d60 Mon Sep 17 00:00:00 2001 From: brreynie Date: Wed, 10 May 2023 11:49:18 +0200 Subject: [PATCH 069/163] generate random color --- .../studeez/screens/subjects/form/SubjectFormViewModel.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index 533123b..e927a13 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -10,6 +10,7 @@ import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +import kotlin.random.Random abstract class SubjectFormViewModel( protected val subjectDAO: SubjectDAO, @@ -42,9 +43,12 @@ class SubjectCreateFormViewModel @Inject constructor( override val uiState = mutableStateOf(SubjectFormUiState()) fun onCreate(openAndPopUp: (String, String) -> Unit) { + val random = Random + val mask: Long = (0x000000FFL shl random.nextInt(0, 3)).inv() + val randomColor = random.nextLong(0xFF000000L, 0xFFFFFFFFL) and mask val newSubject = Subject( name = name, - argb_color = color, + argb_color = randomColor, ) subjectDAO.saveSubject( newSubject From 00751aaf51f6fd3706fd8cb79790bcd918736607 Mon Sep 17 00:00:00 2001 From: brreynie Date: Wed, 10 May 2023 11:59:28 +0200 Subject: [PATCH 070/163] colorgenerator as extension of Color --- .../java/be/ugent/sel/studeez/common/ext/ColorExt.kt | 10 ++++++++++ .../screens/subjects/form/SubjectFormViewModel.kt | 8 +++----- 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/common/ext/ColorExt.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/common/ext/ColorExt.kt b/app/src/main/java/be/ugent/sel/studeez/common/ext/ColorExt.kt new file mode 100644 index 0000000..87ce226 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/common/ext/ColorExt.kt @@ -0,0 +1,10 @@ +package be.ugent.sel.studeez.common.ext + +import androidx.compose.ui.graphics.Color +import kotlin.random.Random + +fun Color.Companion.generateRandomArgb(): Long { + val random = Random + val mask: Long = (0x000000FFL shl random.nextInt(0, 3)).inv() + return random.nextLong(0xFF000000L, 0xFFFFFFFFL) and mask +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index e927a13..eb7ee6f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -2,6 +2,8 @@ package be.ugent.sel.studeez.screens.subjects.form import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.graphics.Color +import be.ugent.sel.studeez.common.ext.generateRandomArgb import be.ugent.sel.studeez.data.SelectedSubject import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.domain.LogService @@ -10,7 +12,6 @@ import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject -import kotlin.random.Random abstract class SubjectFormViewModel( protected val subjectDAO: SubjectDAO, @@ -43,12 +44,9 @@ class SubjectCreateFormViewModel @Inject constructor( override val uiState = mutableStateOf(SubjectFormUiState()) fun onCreate(openAndPopUp: (String, String) -> Unit) { - val random = Random - val mask: Long = (0x000000FFL shl random.nextInt(0, 3)).inv() - val randomColor = random.nextLong(0xFF000000L, 0xFFFFFFFFL) and mask val newSubject = Subject( name = name, - argb_color = randomColor, + argb_color = Color.generateRandomArgb(), ) subjectDAO.saveSubject( newSubject From 81e777f21ddc78b54c50e98b083920d7583de2b8 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Wed, 10 May 2023 12:13:48 +0200 Subject: [PATCH 071/163] #63 Add bio to profile --- .../sel/studeez/data/local/models/User.kt | 8 ++- .../be/ugent/sel/studeez/domain/UserDAO.kt | 10 ++- .../domain/implementation/FirebaseUserDAO.kt | 21 +++++-- .../screens/profile/ProfileEditScreen.kt | 61 ++++++++++++------- .../screens/profile/ProfileEditUiState.kt | 3 +- .../screens/profile/ProfileEditViewModel.kt | 16 ++++- .../studeez/screens/profile/ProfileScreen.kt | 42 +++++++++---- .../screens/profile/ProfileViewModel.kt | 8 ++- .../screens/sign_up/SignUpViewModel.kt | 2 +- app/src/main/res/values/strings.xml | 1 + 10 files changed, 124 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt index 2fba2ce..a92bebb 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/User.kt @@ -1,3 +1,9 @@ package be.ugent.sel.studeez.data.local.models -data class User(val id: String = "") +import com.google.firebase.firestore.DocumentId + +data class User( + @DocumentId val id: String = "", + val username: String = "", + val biography: String = "" +) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index b96cf17..619c77b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -1,9 +1,15 @@ package be.ugent.sel.studeez.domain +import be.ugent.sel.studeez.data.local.models.User + interface UserDAO { - suspend fun getUsername(): String? - suspend fun save(newUsername: String) + suspend fun getUser(): User + + suspend fun saveUser( + newUsername: String, + newBiography: String = "" + ) /** * Delete all references to this user in the database. Similar to the deleteCascade in diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 3158b88..b9421de 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -2,6 +2,7 @@ package be.ugent.sel.studeez.domain.implementation import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager +import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference @@ -14,12 +15,22 @@ class FirebaseUserDAO @Inject constructor( private val auth: AccountDAO ) : UserDAO { - override suspend fun getUsername(): String? { - return currentUserDocument().get().await().getString("username") + override suspend fun getUser(): User { + val userDocument = currentUserDocument().get().await() + return User( + username = userDocument.getString(USERNAME_FIELD) ?: "", + biography = userDocument.getString(BIOGRAPHY_FIELD) ?: "" + ) } - override suspend fun save(newUsername: String) { - currentUserDocument().set(mapOf("username" to newUsername)) + override suspend fun saveUser( + newUsername: String, + newBiography: String + ) { + currentUserDocument().set(mapOf( + USERNAME_FIELD to newUsername, + BIOGRAPHY_FIELD to newBiography + )) } private fun currentUserDocument(): DocumentReference = @@ -27,6 +38,8 @@ class FirebaseUserDAO @Inject constructor( companion object { private const val USER_COLLECTION = "users" + private const val USERNAME_FIELD = "username" + private const val BIOGRAPHY_FIELD = "biography" } override suspend fun deleteUserReferences() { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt index c6fcbaf..950595f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt @@ -1,20 +1,21 @@ package be.ugent.sel.studeez.screens.profile -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicTextButton import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.common.ext.textButton import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.ui.theme.StudeezTheme +import be.ugent.sel.studeez.R.string as AppText data class EditProfileActions( val onUserNameChange: (String) -> Unit, + val onBiographyChange: (String) -> Unit, val onSaveClick: () -> Unit, val onDeleteClick: () -> Unit ) @@ -25,6 +26,7 @@ fun getEditProfileActions( ): EditProfileActions { return EditProfileActions( onUserNameChange = { viewModel.onUsernameChange(it) }, + onBiographyChange = { viewModel.onBiographyChange(it) }, onSaveClick = { viewModel.onSaveClick() }, onDeleteClick = { viewModel.onDeleteClick(openAndPopUp) }, ) @@ -51,28 +53,41 @@ fun EditProfileScreen( editProfileActions: EditProfileActions, ) { SecondaryScreenTemplate( - title = resources().getString(R.string.editing_profile), + title = resources().getString(AppText.editing_profile), popUp = goBack ) { - Column { - LabelledInputField( - value = uiState.username, - onNewValue = editProfileActions.onUserNameChange, - label = R.string.username - ) - BasicTextButton( - text = R.string.save, - Modifier.textButton(), - action = { - editProfileActions.onSaveClick() - goBack() - } - ) - BasicTextButton( - text = R.string.delete_profile, - Modifier.textButton(), - action = editProfileActions.onDeleteClick - ) + LazyColumn { + item { + LabelledInputField( + value = uiState.username, + onNewValue = editProfileActions.onUserNameChange, + label = AppText.username + ) + } + item { + LabelledInputField( + value = uiState.biography, + onNewValue = editProfileActions.onBiographyChange, + label = AppText.biography + ) + } + item { + BasicTextButton( + text = AppText.save, + Modifier.textButton(), + action = { + editProfileActions.onSaveClick() + goBack() + } + ) + } + item { + BasicTextButton( + text = AppText.delete_profile, + Modifier.textButton(), + action = editProfileActions.onDeleteClick + ) + } } } } @@ -81,6 +96,6 @@ fun EditProfileScreen( @Composable fun EditProfileScreenComposable() { StudeezTheme { - EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {})) + EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {}, {})) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt index 9ecaba3..c686c92 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.screens.profile data class ProfileEditUiState ( - val username: String = "" + val username: String = "", + val biography: String = "" ) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt index cb270be..d715e10 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.screens.profile import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager +import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO @@ -23,7 +24,11 @@ class ProfileEditViewModel @Inject constructor( init { launchCatching { - uiState.value = uiState.value.copy(username = userDAO.getUsername()!!) + val user: User = userDAO.getUser() + uiState.value = uiState.value.copy( + username = user.username, + biography = user.biography + ) } } @@ -31,9 +36,16 @@ class ProfileEditViewModel @Inject constructor( uiState.value = uiState.value.copy(username = newValue) } + fun onBiographyChange(newValue: String) { + uiState.value = uiState.value.copy(biography = newValue) + } + fun onSaveClick() { launchCatching { - userDAO.save(uiState.value.username) + userDAO.saveUser( + newUsername = uiState.value.username, + newBiography = uiState.value.biography + ) SnackbarManager.showMessage(R.string.success) } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt index 9c76337..3652c70 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt @@ -1,17 +1,18 @@ package be.ugent.sel.studeez.screens.profile +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit -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.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview -import be.ugent.sel.studeez.R +import androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.composable.Headline import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate import be.ugent.sel.studeez.common.composable.drawer.DrawerActions @@ -22,16 +23,18 @@ import be.ugent.sel.studeez.R.string as AppText data class ProfileActions( val getUsername: suspend CoroutineScope.() -> String?, - val onEditProfileClick: () -> Unit, + val getBiography: suspend CoroutineScope.() -> String?, + val onEditProfileClick: () -> Unit ) fun getProfileActions( viewModel: ProfileViewModel, - open: (String) -> Unit, + open: (String) -> Unit ): ProfileActions { return ProfileActions( getUsername = { viewModel.getUsername() }, - onEditProfileClick = { viewModel.onEditProfileClick(open) }, + getBiography = { viewModel.getBiography() }, + onEditProfileClick = { viewModel.onEditProfileClick(open) } ) } @@ -56,8 +59,10 @@ fun ProfileScreen( navigationBarActions: NavigationBarActions, ) { var username: String? by remember { mutableStateOf("") } + var biography: String? by remember { mutableStateOf("") } LaunchedEffect(key1 = Unit) { username = profileActions.getUsername(this) + biography = profileActions.getBiography(this) } PrimaryScreenTemplate( title = resources().getString(AppText.profile), @@ -65,7 +70,20 @@ fun ProfileScreen( navigationBarActions = navigationBarActions, barAction = { EditAction(onClick = profileActions.onEditProfileClick) } ) { - Headline(text = (username ?: resources().getString(R.string.no_username))) + LazyColumn( + verticalArrangement = Arrangement.spacedBy(15.dp) + ) { + item { + Headline(text = username ?: resources().getString(AppText.no_username)) + } + item { + Text( + text = biography ?: "", + textAlign = TextAlign.Center, + modifier = Modifier.padding(48.dp, 0.dp) + ) + } + } } } @@ -86,7 +104,7 @@ fun EditAction( @Composable fun ProfileScreenPreview() { ProfileScreen( - profileActions = ProfileActions({ null }, {}), + profileActions = ProfileActions({ null }, { null }, {}), drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index e24defd..760b962 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -13,8 +13,12 @@ class ProfileViewModel @Inject constructor( logService: LogService ) : StudeezViewModel(logService) { - suspend fun getUsername(): String? { - return userDAO.getUsername() + suspend fun getUsername(): String { + return userDAO.getUser().username + } + + suspend fun getBiography(): String { + return userDAO.getUser().biography } fun onEditProfileClick(open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt index a08d063..2a448c8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt @@ -66,7 +66,7 @@ class SignUpViewModel @Inject constructor( launchCatching { accountDAO.signUpWithEmailAndPassword(email, password) accountDAO.signInWithEmailAndPassword(email, password) - userDAO.save(username) + userDAO.saveUser(username) openAndPopUp(HOME_SCREEN, SIGN_UP_SCREEN) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d51259c..0ed2693 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,6 +54,7 @@ Edit profile Editing profile Delete profile + Bio From 4419c86b050b5e403e984939edb69c4600e9e20d Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Wed, 10 May 2023 12:38:58 +0200 Subject: [PATCH 072/163] Unify DAO names --- .../main/java/be/ugent/sel/studeez/di/DatabaseModule.kt | 6 +++--- .../{FireBaseCollections.kt => FirebaseCollections.kt} | 2 +- .../{FireBaseSessionDAO.kt => FirebaseSessionDAO.kt} | 6 +++--- .../{FireBaseSubjectDAO.kt => FirebaseSubjectDAO.kt} | 6 +++--- .../{FireBaseTaskDAO.kt => FirebaseTaskDAO.kt} | 8 ++++---- .../sel/studeez/domain/implementation/FirebaseTimerDAO.kt | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseCollections.kt => FirebaseCollections.kt} (90%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseSessionDAO.kt => FirebaseSessionDAO.kt} (87%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseSubjectDAO.kt => FirebaseSubjectDAO.kt} (87%) rename app/src/main/java/be/ugent/sel/studeez/domain/implementation/{FireBaseTaskDAO.kt => FirebaseTaskDAO.kt} (87%) diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index 7ee4992..93dea2d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -26,11 +26,11 @@ abstract class DatabaseModule { abstract fun provideConfigurationService(impl: FirebaseConfigurationService): ConfigurationService @Binds - abstract fun provideSessionDAO(impl: FireBaseSessionDAO): SessionDAO + abstract fun provideSessionDAO(impl: FirebaseSessionDAO): SessionDAO @Binds - abstract fun provideSubjectDAO(impl: FireBaseSubjectDAO): SubjectDAO + abstract fun provideSubjectDAO(impl: FirebaseSubjectDAO): SubjectDAO @Binds - abstract fun provideTaskDAO(impl: FireBaseTaskDAO): TaskDAO + abstract fun provideTaskDAO(impl: FirebaseTaskDAO): TaskDAO } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt similarity index 90% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt index 78867c9..fcdc0a4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseCollections.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt @@ -1,6 +1,6 @@ package be.ugent.sel.studeez.domain.implementation -object FireBaseCollections { +object FirebaseCollections { const val SESSION_COLLECTION = "sessions" const val USER_COLLECTION = "users" const val TIMER_COLLECTION = "timers" diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt index a818236..ca7c6aa 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSessionDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSessionDAO.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseSessionDAO @Inject constructor( +class FirebaseSessionDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO ) : SessionDAO { @@ -31,7 +31,7 @@ class FireBaseSessionDAO @Inject constructor( } private fun currentUserSessionsCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SESSION_COLLECTION) + .collection(FirebaseCollections.SESSION_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt index 7d90fbf..fc93da1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseSubjectDAO.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseSubjectDAO @Inject constructor( +class FirebaseSubjectDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO, ) : SubjectDAO { @@ -33,7 +33,7 @@ class FireBaseSubjectDAO @Inject constructor( } private fun currentUserSubjectsCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SUBJECT_COLLECTION) + .collection(FirebaseCollections.SUBJECT_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt rename to app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt index b8855e6..d0185ff 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseTaskDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTaskDAO.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject -class FireBaseTaskDAO @Inject constructor( +class FirebaseTaskDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO, ) : TaskDAO { @@ -41,9 +41,9 @@ class FireBaseTaskDAO @Inject constructor( } private fun selectedSubjectTasksCollection(subjectId: String): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.SUBJECT_COLLECTION) + .collection(FirebaseCollections.SUBJECT_COLLECTION) .document(subjectId) - .collection(FireBaseCollections.TASK_COLLECTION) + .collection(FirebaseCollections.TASK_COLLECTION) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt index 1f37a18..dad7047 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt @@ -48,8 +48,8 @@ class FirebaseTimerDAO @Inject constructor( } private fun currentUserTimersCollection(): CollectionReference = - firestore.collection(FireBaseCollections.USER_COLLECTION) + firestore.collection(FirebaseCollections.USER_COLLECTION) .document(auth.currentUserId) - .collection(FireBaseCollections.TIMER_COLLECTION) + .collection(FirebaseCollections.TIMER_COLLECTION) } \ No newline at end of file From d6496cb8ad33c611df326480f15ed1f31094a157 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 12:44:51 +0200 Subject: [PATCH 073/163] changed labeled error field to take some states --- .../common/composable/TextFieldComposable.kt | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt index e3c54ef..9922985 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt @@ -3,7 +3,6 @@ package be.ugent.sel.studeez.common.composable import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.* import androidx.compose.material.icons.Icons @@ -22,7 +21,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.resources -import kotlin.math.sin import be.ugent.sel.studeez.R.drawable as AppIcon import be.ugent.sel.studeez.R.string as AppText @@ -119,7 +117,9 @@ fun LabeledErrorTextField( initialValue: String, @StringRes label: Int, singleLine: Boolean = false, - errorText: Int, + isValid: MutableState, + isFirst: MutableState = remember { mutableStateOf(false) }, + @StringRes errorText: Int, keyboardType: KeyboardType, predicate: (String) -> Boolean, onNewCorrectValue: (String) -> Unit @@ -128,31 +128,28 @@ fun LabeledErrorTextField( mutableStateOf(initialValue) } - var isValid by remember { - mutableStateOf(predicate(value)) - } - Column { OutlinedTextField( modifier = modifier.fieldModifier(), value = value, onValueChange = { newText -> + isFirst.value = false value = newText - isValid = predicate(value) - if (isValid) { + isValid.value = predicate(value) + if (isValid.value) { onNewCorrectValue(newText) } }, singleLine = singleLine, label = { Text(text = stringResource(id = label)) }, - isError = !isValid, + isError = !isValid.value && !isFirst.value, keyboardOptions = KeyboardOptions( keyboardType = keyboardType, imeAction = ImeAction.Done ) ) - if (!isValid) { + if (!isValid.value && !isFirst.value) { Text( modifier = Modifier.padding(start = 16.dp), text = stringResource(id = errorText), From b4166386a56e4e4c736745d800c23032f2944f04 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 10 May 2023 12:46:30 +0200 Subject: [PATCH 074/163] added error input field for name and discription --- .../form_screens/AbstractTimerFormScreen.kt | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt index 168a2f8..e936898 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt @@ -13,8 +13,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicButton +import be.ugent.sel.studeez.common.composable.LabeledErrorTextField import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo @@ -28,8 +30,14 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { extraButton: @Composable () -> Unit ) { + var name by remember { mutableStateOf(timerInfo.name) } + val isNameValid = remember { mutableStateOf(false) } + val hasEditedName = remember { mutableStateOf(true) } + var description by remember { mutableStateOf(timerInfo.description) } + val isDescriptionValid = remember { mutableStateOf(false) } + val hasEditedDescription = remember { mutableStateOf(true) } // This shall rerun whenever name and description change timerInfo.name = name @@ -47,18 +55,30 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { ) { // Fields that every timer shares (ommited id) - LabelledInputField( - value = name, - onNewValue = { name = it }, + LabeledErrorTextField( + initialValue = timerInfo.name, label = R.string.name, - ) + errorText = AppText.name_error, + isValid = isNameValid, + isFirst = hasEditedName, + keyboardType = KeyboardType.Text, + predicate = { it.isNotBlank() } + ) { correctName -> + name = correctName + } - LabelledInputField( - value = description, - onNewValue = { description = it }, - label = AppText.description, - singleLine = false - ) + LabeledErrorTextField( + initialValue = timerInfo.description, + label = R.string.description, + errorText = AppText.description_error, + isValid = isDescriptionValid, + isFirst = hasEditedDescription, + singleLine= false, + keyboardType = KeyboardType.Text, + predicate = { it.isNotBlank() } + ) { correctName -> + description = correctName + } ExtraFields() @@ -66,7 +86,12 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { Column { BasicButton(R.string.save, Modifier.basicButton()) { - onSaveClick(timerInfo) + if (isNameValid.value && isDescriptionValid.value) { + onSaveClick(timerInfo) + } else { + hasEditedName.value = false + hasEditedDescription.value = false + } } extraButton() } From d9f4170fbd24c173ee217d90bcbe858dd0c31c98 Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Wed, 10 May 2023 15:10:01 +0200 Subject: [PATCH 075/163] emojis in sessionrecap --- .idea/misc.xml | 1 - .../FloatingActionButtonComposable.kt | 1 - .../common/composable/ImageComposable.kt | 39 +++++++++++ .../session_recap/SessionRecapScreen.kt | 67 ++++++++++++++++++- app/src/main/res/drawable/mood_1.xml | 5 ++ app/src/main/res/drawable/mood_2.xml | 5 ++ app/src/main/res/values/strings.xml | 7 ++ 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/common/composable/ImageComposable.kt create mode 100644 app/src/main/res/drawable/mood_1.xml create mode 100644 app/src/main/res/drawable/mood_2.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt index bc40ead..ea2b52d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/FloatingActionButtonComposable.kt @@ -2,7 +2,6 @@ package be.ugent.sel.studeez.common.composable import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.updateTransition -import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.material.FloatingActionButton import androidx.compose.material.Icon diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ImageComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ImageComposable.kt new file mode 100644 index 0000000..39e7272 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ImageComposable.kt @@ -0,0 +1,39 @@ +package be.ugent.sel.studeez.common.composable + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.unit.dp + +@Composable +fun ImageBackgroundButton( + paint: Painter, + str: String, + background2: Color, + setBackground1: (Color) -> Unit, + setBackground2: (Color) -> Unit +) { + Image( + painter = paint, + str, + modifier = Modifier + .clickable { + if (background2 == Color.Transparent) { + setBackground1(Color.LightGray) + setBackground2(Color.Transparent) + } else { + setBackground2(Color.Transparent) + } + } + .border( + width = 2.dp, + color = background2, + shape = RoundedCornerShape(16.dp) + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt index 2d06e0b..1e02ad9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt @@ -1,13 +1,30 @@ package be.ugent.sel.studeez.screens.session_recap +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.material.ButtonDefaults import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicButton +import be.ugent.sel.studeez.common.composable.ImageBackgroundButton import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.data.local.models.SessionReport import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds @@ -47,10 +64,49 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi val sessionReport: SessionReport = sessionRecapActions.getSessionReport() val studyTime: Int = sessionReport.studyTime val hms: HoursMinutesSeconds = Time(studyTime).getAsHMS() + val (background1, setBackground1) = remember { mutableStateOf(Color.Transparent) } + val (background2, setBackground2) = remember { mutableStateOf(Color.Transparent) } Column( modifier = modifier + .fillMaxWidth() + .fillMaxHeight() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center ) { - Text(text = "You studied: $hms") + Text( + text = stringResource(R.string.congrats) + hms, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Light, + fontSize = 30.sp + ) + + Text( + text = stringResource(R.string.how_did_it_go), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Light, + fontSize = 30.sp + ) + + Row { + ImageBackgroundButton( + paint = painterResource(id = R.drawable.mood_1), + str = stringResource(id = R.string.good), + background2 = background2, + setBackground1 = setBackground2, + setBackground2 = setBackground1 + ) + + ImageBackgroundButton( + paint = painterResource(id = R.drawable.mood_2), + str = stringResource(id = R.string.bad), + background2 = background1, + setBackground1 = setBackground1, + setBackground2 = setBackground2 + ) + } BasicButton( R.string.save, Modifier.basicButton() @@ -65,3 +121,12 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi } } } + +@Preview +@Composable +fun SessionRecapScreenPreview() { + SessionRecapScreen( + modifier = Modifier, + sessionRecapActions = SessionRecapActions(hiltViewModel(), {}, {}) + ) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/mood_1.xml b/app/src/main/res/drawable/mood_1.xml new file mode 100644 index 0000000..bf009f2 --- /dev/null +++ b/app/src/main/res/drawable/mood_1.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/mood_2.xml b/app/src/main/res/drawable/mood_2.xml new file mode 100644 index 0000000..0fd3daa --- /dev/null +++ b/app/src/main/res/drawable/mood_2.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ec0e9d..9ebb196 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -138,4 +138,11 @@ Break Time Number of Repeats + + "Congratulations! You studied: " + How did it go? + Good + Bad + + From 6bef378fc043d8566fd56301a8477bdbc34d040a Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Wed, 10 May 2023 15:32:39 +0200 Subject: [PATCH 076/163] correct aligment --- .../session_recap/SessionRecapScreen.kt | 83 +++++++++++-------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt index 1e02ad9..06a15e1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt @@ -72,52 +72,65 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi .fillMaxHeight() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.SpaceBetween ) { Text( text = stringResource(R.string.congrats) + hms, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth(), textAlign = TextAlign.Center, fontWeight = FontWeight.Light, - fontSize = 30.sp + fontSize = 30.sp, + ) - Text( - text = stringResource(R.string.how_did_it_go), - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Light, - fontSize = 30.sp - ) - - Row { - ImageBackgroundButton( - paint = painterResource(id = R.drawable.mood_1), - str = stringResource(id = R.string.good), - background2 = background2, - setBackground1 = setBackground2, - setBackground2 = setBackground1 + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = stringResource(R.string.how_did_it_go), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Light, + fontSize = 30.sp ) - ImageBackgroundButton( - paint = painterResource(id = R.drawable.mood_2), - str = stringResource(id = R.string.bad), - background2 = background1, - setBackground1 = setBackground1, - setBackground2 = setBackground2 - ) + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .align(Alignment.CenterHorizontally) + ) { + ImageBackgroundButton( + paint = painterResource(id = R.drawable.mood_1), + str = stringResource(id = R.string.good), + background2 = background2, + setBackground1 = setBackground2, + setBackground2 = setBackground1 + ) + + ImageBackgroundButton( + paint = painterResource(id = R.drawable.mood_2), + str = stringResource(id = R.string.bad), + background2 = background1, + setBackground1 = setBackground1, + setBackground2 = setBackground2 + ) + } } - BasicButton( - R.string.save, Modifier.basicButton() - ) { - sessionRecapActions.saveSession() - } - BasicButton( - R.string.discard, Modifier.basicButton(), - colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red) - ) { - sessionRecapActions.discardSession() + Column { + BasicButton( + R.string.save, Modifier.basicButton() + ) { + sessionRecapActions.saveSession() + } + BasicButton( + R.string.discard, Modifier.basicButton(), + colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red) + ) { + sessionRecapActions.discardSession() + } } } } From 45efd5cb3f82d7abeb68edbaef14b28501c3737a Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Wed, 10 May 2023 15:37:34 +0200 Subject: [PATCH 077/163] small fix --- .../sel/studeez/screens/session_recap/SessionRecapScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt index 06a15e1..72082c8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt @@ -142,4 +142,4 @@ fun SessionRecapScreenPreview() { modifier = Modifier, sessionRecapActions = SessionRecapActions(hiltViewModel(), {}, {}) ) -} \ No newline at end of file +} From de25ed85b53746ae704d87c1a3729afacbe1554a Mon Sep 17 00:00:00 2001 From: brreynie Date: Wed, 10 May 2023 15:50:54 +0200 Subject: [PATCH 078/163] #99 fix subjectentry to use flows for computed values --- .../common/composable/tasks/SubjectEntry.kt | 16 +++++++--- .../studeez/data/local/models/task/Subject.kt | 4 --- .../be/ugent/sel/studeez/domain/SubjectDAO.kt | 4 +-- .../implementation/FireBaseSubjectDAO.kt | 31 +++++-------------- .../studeez/screens/subjects/SubjectScreen.kt | 13 ++++++-- .../screens/subjects/SubjectViewModel.kt | 8 +++++ 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt index 5db2af3..c6631ce 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/tasks/SubjectEntry.kt @@ -31,9 +31,13 @@ import be.ugent.sel.studeez.R.string as AppText fun SubjectEntry( subject: Subject, onViewSubject: () -> Unit, + getTaskCount: () -> Flow, + getCompletedTaskCount: () -> Flow, getStudyTime: () -> Flow, ) { val studytime by getStudyTime().collectAsState(initial = 0) + val taskCount by getTaskCount().collectAsState(initial = 0) + val completedTaskCount by getCompletedTaskCount().collectAsState(initial = 0) Card( modifier = Modifier .fillMaxWidth() @@ -80,7 +84,7 @@ fun SubjectEntry( imageVector = Icons.Default.List, contentDescription = stringResource(id = AppText.tasks) ) - Text(text = "${subject.taskCompletedCount}/${subject.taskCount}") + Text(text = "${completedTaskCount}/${taskCount}") } } } @@ -104,11 +108,11 @@ fun SubjectEntryPreview() { subject = Subject( name = "Test Subject", argb_color = 0xFFFFD200, - taskCount = 5, - taskCompletedCount = 2, ), onViewSubject = {}, - getStudyTime = { flowOf() } + getTaskCount = { flowOf() }, + getCompletedTaskCount = { flowOf() }, + getStudyTime = { flowOf() }, ) } @@ -121,6 +125,8 @@ fun OverflowSubjectEntryPreview() { argb_color = 0xFFFFD200, ), onViewSubject = {}, - getStudyTime = { flowOf() } + getTaskCount = { flowOf() }, + getCompletedTaskCount = { flowOf() }, + getStudyTime = { flowOf() }, ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt index 74ebe9f..261f3e0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/task/Subject.kt @@ -8,10 +8,6 @@ data class Subject( val name: String = "", val argb_color: Long = 0, var archived: Boolean = false, - @get:Exclude @set:Exclude - var taskCount: Int = 0, - @get:Exclude @set:Exclude - var taskCompletedCount: Int = 0, ) object SubjectDocument { diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt index bad8106..c9f6a8a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt @@ -13,8 +13,8 @@ interface SubjectDAO { fun updateSubject(newSubject: Subject) - suspend fun getTaskCount(subject: Subject): Int - suspend fun getCompletedTaskCount(subject: Subject): Int + fun getTaskCount(subject: Subject): Flow + fun getCompletedTaskCount(subject: Subject): Flow fun getStudyTime(subject: Subject): Flow suspend fun getSubject(subjectId: String): Subject? diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt index b023986..e8931ed 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt @@ -2,10 +2,10 @@ 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.SubjectDocument +import be.ugent.sel.studeez.data.local.models.task.Task import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.SubjectDAO import be.ugent.sel.studeez.domain.TaskDAO -import com.google.firebase.firestore.AggregateSource import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query @@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.tasks.await import javax.inject.Inject +import kotlin.collections.count class FireBaseSubjectDAO @Inject constructor( private val firestore: FirebaseFirestore, @@ -26,13 +27,6 @@ class FireBaseSubjectDAO @Inject constructor( .subjectNotArchived() .snapshots() .map { it.toObjects(Subject::class.java) } - .map { subjects -> - subjects.map { subject -> - subject.taskCount = getTaskCount(subject) - subject.taskCompletedCount = getCompletedTaskCount(subject) - subject - } - } } override suspend fun getSubject(subjectId: String): Subject? { @@ -51,23 +45,14 @@ class FireBaseSubjectDAO @Inject constructor( currentUserSubjectsCollection().document(newSubject.id).set(newSubject) } - override suspend fun getTaskCount(subject: Subject): Int { - return subjectTasksCollection(subject) - .taskNotArchived() - .count() - .get(AggregateSource.SERVER) - .await() - .count.toInt() + override fun getTaskCount(subject: Subject): Flow { + return taskDAO.getTasks(subject) + .map(List::count) } - override suspend fun getCompletedTaskCount(subject: Subject): Int { - return subjectTasksCollection(subject) - .taskNotArchived() - .taskNotCompleted() - .count() - .get(AggregateSource.SERVER) - .await() - .count.toInt() + override fun getCompletedTaskCount(subject: Subject): Flow { + return taskDAO.getTasks(subject) + .map { tasks -> tasks.count { it.completed && !it.archived } } } override fun getStudyTime(subject: Subject): Flow { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt index ab2cff4..852c0e5 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectScreen.kt @@ -36,6 +36,8 @@ fun SubjectRoute( navigationBarActions = navigationBarActions, onAddSubject = { viewModel.onAddSubject(open) }, onViewSubject = { viewModel.onViewSubject(it, open) }, + getTaskCount = viewModel::getTaskCount, + getCompletedTaskCount = viewModel::getCompletedTaskCount, getStudyTime = viewModel::getStudyTime, uiState, ) @@ -47,6 +49,8 @@ fun SubjectScreen( navigationBarActions: NavigationBarActions, onAddSubject: () -> Unit, onViewSubject: (Subject) -> Unit, + getTaskCount: (Subject) -> Flow, + getCompletedTaskCount: (Subject) -> Flow, getStudyTime: (Subject) -> Flow, uiState: SubjectUiState, ) { @@ -76,6 +80,8 @@ fun SubjectScreen( SubjectEntry( subject = it, onViewSubject = { onViewSubject(it) }, + getTaskCount = { getTaskCount(it) }, + getCompletedTaskCount = { getCompletedTaskCount(it) }, getStudyTime = { getStudyTime(it) }, ) } @@ -94,13 +100,14 @@ fun SubjectScreenPreview() { navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), onAddSubject = {}, onViewSubject = {}, + getTaskCount = { flowOf() }, + getCompletedTaskCount = { flowOf() }, getStudyTime = { flowOf() }, uiState = SubjectUiState.Succes( listOf( Subject( name = "Test Subject", argb_color = 0xFFFFD200, - taskCount = 5, taskCompletedCount = 2, ) ) ) @@ -115,7 +122,9 @@ fun SubjectScreenLoadingPreview() { navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}), onAddSubject = {}, onViewSubject = {}, + getTaskCount = { flowOf() }, + getCompletedTaskCount = { flowOf() }, getStudyTime = { flowOf() }, - uiState = SubjectUiState.Loading + uiState = SubjectUiState.Loading, ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt index c158529..19ba396 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/SubjectViewModel.kt @@ -30,6 +30,14 @@ class SubjectViewModel @Inject constructor( open(StudeezDestinations.ADD_SUBJECT_FORM) } + fun getTaskCount(subject: Subject): Flow { + return subjectDAO.getTaskCount(subject) + } + + fun getCompletedTaskCount(subject: Subject): Flow { + return subjectDAO.getCompletedTaskCount(subject) + } + fun getStudyTime(subject: Subject): Flow { return subjectDAO.getStudyTime(subject) } From a7752e5706128163c8210dec9e906f358c2ad1ad Mon Sep 17 00:00:00 2001 From: brreynie Date: Wed, 10 May 2023 16:43:14 +0200 Subject: [PATCH 079/163] #99 fix update subject name after edit --- .../screens/subjects/form/SubjectFormViewModel.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index 533123b..7c2f601 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -75,11 +75,13 @@ class SubjectEditFormViewModel @Inject constructor( } fun onEdit(openAndPopUp: (String, String) -> Unit) { - val newSubject = selectedSubject().copy( - name = name, - argb_color = color, + selectedSubject.set( + selectedSubject().copy( + name = name, + argb_color = color, + ) ) - subjectDAO.updateSubject(newSubject) + subjectDAO.updateSubject(selectedSubject()) openAndPopUp(StudeezDestinations.TASKS_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) } } \ No newline at end of file From 61de50ea557bcf2f736b718a326210f7db7fe806 Mon Sep 17 00:00:00 2001 From: brreynie Date: Wed, 10 May 2023 16:52:41 +0200 Subject: [PATCH 080/163] reformat and optimize imports --- .../screens/session_recap/SessionRecapScreen.kt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt index 72082c8..f3a5739 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt @@ -1,11 +1,6 @@ package be.ugent.sel.studeez.screens.session_recap -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.material.ButtonDefaults import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -42,8 +37,8 @@ fun getSessionRecapActions( ): SessionRecapActions { return SessionRecapActions( viewModel::getSessionReport, - {viewModel.saveSession(openAndPopUp)}, - {viewModel.discardSession(openAndPopUp)} + { viewModel.saveSession(openAndPopUp) }, + { viewModel.discardSession(openAndPopUp) } ) } @@ -82,7 +77,7 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi fontWeight = FontWeight.Light, fontSize = 30.sp, - ) + ) Column( modifier = Modifier.fillMaxWidth() From 93aae20f85bc9390082e95e84b019dcaf5880693 Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 13:22:12 +0200 Subject: [PATCH 081/163] #109 archiving subject archives all tasks of subject --- .../java/be/ugent/sel/studeez/domain/SubjectDAO.kt | 2 ++ .../domain/implementation/FireBaseSubjectDAO.kt | 14 ++++++++++++++ .../screens/subjects/form/SubjectFormScreen.kt | 7 ++++++- .../screens/subjects/form/SubjectFormViewModel.kt | 6 ++++-- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt index c9f6a8a..d887ef5 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/SubjectDAO.kt @@ -13,6 +13,8 @@ interface SubjectDAO { fun updateSubject(newSubject: Subject) + suspend fun archiveSubject(subject: Subject) + fun getTaskCount(subject: Subject): Flow fun getCompletedTaskCount(subject: Subject): Flow fun getStudyTime(subject: Subject): Flow diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt index e8931ed..66815dc 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FireBaseSubjectDAO.kt @@ -1,8 +1,10 @@ package be.ugent.sel.studeez.domain.implementation +import android.util.Log import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.data.local.models.task.SubjectDocument import be.ugent.sel.studeez.data.local.models.task.Task +import be.ugent.sel.studeez.data.local.models.task.TaskDocument import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.SubjectDAO import be.ugent.sel.studeez.domain.TaskDAO @@ -45,6 +47,18 @@ class FireBaseSubjectDAO @Inject constructor( currentUserSubjectsCollection().document(newSubject.id).set(newSubject) } + override suspend fun archiveSubject(subject: Subject) { + currentUserSubjectsCollection().document(subject.id).update(SubjectDocument.archived, true) + currentUserSubjectsCollection().document(subject.id) + .collection(FireBaseCollections.TASK_COLLECTION) + .taskNotArchived() + .get().await() + .documents + .forEach { + it.reference.update(TaskDocument.archived, true) + } + } + override fun getTaskCount(subject: Subject): Flow { return taskDAO.getTasks(subject) .map(List::count) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt index 19e6816..bd81c8c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource @@ -16,6 +17,7 @@ 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 kotlinx.coroutines.launch import be.ugent.sel.studeez.R.string as AppText @Composable @@ -42,6 +44,7 @@ fun SubjectEditRoute( viewModel: SubjectEditFormViewModel, ) { val uiState by viewModel.uiState + val coroutineScope = rememberCoroutineScope() SubjectForm( title = AppText.edit_subject, goBack = goBack, @@ -51,7 +54,9 @@ fun SubjectEditRoute( onColorChange = {}, ) { DeleteButton(text = AppText.delete_subject) { - viewModel.onDelete(openAndPopUp) + coroutineScope.launch { + viewModel.onDelete(openAndPopUp) + } } } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index 7c2f601..84162d0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -6,6 +6,7 @@ import be.ugent.sel.studeez.data.SelectedSubject import be.ugent.sel.studeez.data.local.models.task.Subject import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.SubjectDAO +import be.ugent.sel.studeez.domain.TaskDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -59,6 +60,7 @@ class SubjectCreateFormViewModel @Inject constructor( @HiltViewModel class SubjectEditFormViewModel @Inject constructor( subjectDAO: SubjectDAO, + private val taskDAO: TaskDAO, selectedSubject: SelectedSubject, logService: LogService, ) : SubjectFormViewModel(subjectDAO, selectedSubject, logService) { @@ -69,8 +71,8 @@ class SubjectEditFormViewModel @Inject constructor( ) ) - fun onDelete(openAndPopUp: (String, String) -> Unit) { - subjectDAO.updateSubject(selectedSubject().copy(archived = true)) + suspend fun onDelete(openAndPopUp: (String, String) -> Unit) { + subjectDAO.archiveSubject(selectedSubject()) openAndPopUp(StudeezDestinations.SUBJECT_SCREEN, StudeezDestinations.EDIT_SUBJECT_FORM) } From a328d2194f2fd62d4da27d9933050fd6a8ba6e51 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 15:36:04 +0200 Subject: [PATCH 082/163] isValid is optional and by default true --- .../ugent/sel/studeez/common/composable/TextFieldComposable.kt | 2 +- .../screens/timer_form/form_screens/BreakTimerFormScreen.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt index 9922985..47bdec5 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt @@ -117,7 +117,7 @@ fun LabeledErrorTextField( initialValue: String, @StringRes label: Int, singleLine: Boolean = false, - isValid: MutableState, + isValid: MutableState = remember { mutableStateOf(true) }, isFirst: MutableState = remember { mutableStateOf(false) }, @StringRes errorText: Int, keyboardType: KeyboardType, diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt index 21e862d..333fc24 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt @@ -30,6 +30,7 @@ class BreakTimerFormScreen( initialValue = breakTimerInfo.repeats.toString(), label = R.string.repeats, errorText = AppText.repeats_error, + isValid = mutableStateOf(false), keyboardType = KeyboardType.Decimal, predicate = { it.matches(Regex("[1-9]+\\d*")) } ) { correctlyTypedInt -> From d666733abea05e26000b1145ae1039ea6b3878ae Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 15:51:50 +0200 Subject: [PATCH 083/163] #109 fix pomodorotimer doing too many cycles --- .../local/models/timer_functional/FunctionalPomodoroTimer.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt index 765fbcd..f5237d6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt @@ -6,14 +6,13 @@ class FunctionalPomodoroTimer( val repeats: Int ) : FunctionalTimer(studyTime) { - var breaksRemaining = repeats + var breaksRemaining = repeats - 1 var isInBreak = false override fun tick() { if (hasEnded()) { return } - if (hasCurrentCountdownEnded()) { if (isInBreak) { breaksRemaining-- From ad0780416db5b6a1464342cf8a1343ab2042a6b1 Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 15:54:59 +0200 Subject: [PATCH 084/163] fix dots not showing up correctly --- .../screens/session/sessionScreens/BreakSessionScreen.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt index 9c59b46..f49a60b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt @@ -37,11 +37,11 @@ class BreakSessionScreen( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { - repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) { + repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining - 1) { Dot(color = Color.DarkGray) } if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) - repeat(funPomoDoroTimer.breaksRemaining - 1) { + repeat(funPomoDoroTimer.breaksRemaining) { Dot(color = Color.Gray) } } From 5f0bdc948fc35e9b916f97dc5f3aaf367a7c2a4d Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 15:57:20 +0200 Subject: [PATCH 085/163] all dots green when ended --- .../sessionScreens/BreakSessionScreen.kt | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt index f49a60b..f328c5f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt @@ -4,18 +4,13 @@ import android.media.MediaPlayer import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import be.ugent.sel.studeez.R import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.R.string as AppText @@ -23,7 +18,7 @@ import be.ugent.sel.studeez.R.string as AppText class BreakSessionScreen( private val funPomoDoroTimer: FunctionalPomodoroTimer, private var mediaplayer: MediaPlayer? -): AbstractSessionScreen() { +) : AbstractSessionScreen() { @Composable override fun MidSection() { @@ -37,23 +32,31 @@ class BreakSessionScreen( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { - repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining - 1) { - Dot(color = Color.DarkGray) - } - if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) - repeat(funPomoDoroTimer.breaksRemaining) { - Dot(color = Color.Gray) + if (funPomoDoroTimer.hasEnded()) { + repeat(funPomoDoroTimer.repeats) { + Dot(Color.Green) + } + } else { + repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining - 1) { + Dot(color = Color.DarkGray) + } + if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) + repeat(funPomoDoroTimer.breaksRemaining) { + Dot(color = Color.Gray) + } } } } @Composable private fun Dot(color: Color) { - Box(modifier = Modifier - .padding(5.dp) - .size(10.dp) - .clip(CircleShape) - .background(color)) + Box( + modifier = Modifier + .padding(5.dp) + .size(10.dp) + .clip(CircleShape) + .background(color) + ) } @Composable From 0341fcf137c66987f713c4bde5298f0fdadea866 Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 16:31:03 +0200 Subject: [PATCH 086/163] clear navStack when ending session --- .../be/ugent/sel/studeez/navigation/StudeezNavGraph.kt | 5 +++-- .../screens/session_recap/SessionRecapScreen.kt | 10 +++++----- .../screens/session_recap/SessionRecapViewModel.kt | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index 37085f1..0a0e8b4 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -25,9 +25,9 @@ import be.ugent.sel.studeez.screens.settings.SettingsRoute import be.ugent.sel.studeez.screens.sign_up.SignUpRoute import be.ugent.sel.studeez.screens.splash.SplashRoute import be.ugent.sel.studeez.screens.subjects.SubjectRoute -import be.ugent.sel.studeez.screens.tasks.TaskRoute import be.ugent.sel.studeez.screens.subjects.form.SubjectCreateRoute import be.ugent.sel.studeez.screens.subjects.form.SubjectEditRoute +import be.ugent.sel.studeez.screens.tasks.TaskRoute import be.ugent.sel.studeez.screens.tasks.form.TaskCreateRoute import be.ugent.sel.studeez.screens.tasks.form.TaskEditRoute import be.ugent.sel.studeez.screens.timer_form.TimerAddRoute @@ -51,6 +51,7 @@ fun StudeezNavGraph( val open: (String) -> Unit = { appState.navigate(it) } val openAndPopUp: (String, String) -> Unit = { route, popUp -> appState.navigateAndPopUp(route, popUp) } + val clearAndNavigate: (route: String) -> Unit = { route -> appState.clearAndNavigate(route) } val drawerActions: DrawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp) val navigationBarActions: NavigationBarActions = @@ -200,7 +201,7 @@ fun StudeezNavGraph( composable(StudeezDestinations.SESSION_RECAP) { SessionRecapRoute( - openAndPopUp = openAndPopUp, + clearAndNavigate = clearAndNavigate, viewModel = hiltViewModel() ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt index 2d06e0b..e49c8d6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt @@ -21,24 +21,24 @@ data class SessionRecapActions( fun getSessionRecapActions( viewModel: SessionRecapViewModel, - openAndPopUp: (String, String) -> Unit, + clearAndNavigate: (String) -> Unit, ): SessionRecapActions { return SessionRecapActions( viewModel::getSessionReport, - {viewModel.saveSession(openAndPopUp)}, - {viewModel.discardSession(openAndPopUp)} + { viewModel.saveSession(clearAndNavigate) }, + { viewModel.discardSession(clearAndNavigate) } ) } @Composable fun SessionRecapRoute( - openAndPopUp: (String, String) -> Unit, + clearAndNavigate: (String) -> Unit, modifier: Modifier = Modifier, viewModel: SessionRecapViewModel, ) { SessionRecapScreen( modifier = modifier, - getSessionRecapActions(viewModel, openAndPopUp) + getSessionRecapActions(viewModel, clearAndNavigate) ) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt index c11e28f..bf11b93 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapViewModel.kt @@ -24,15 +24,15 @@ class SessionRecapViewModel @Inject constructor( return selectedSessionReport() } - fun saveSession(open: (String, String) -> Unit) { + fun saveSession(open: (String) -> Unit) { sessionDAO.saveSession(getSessionReport()) val newTask = selectedTask().copy(time = selectedTask().time + selectedSessionReport().studyTime) taskDAO.updateTask(newTask) - open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) + open(StudeezDestinations.HOME_SCREEN) } - fun discardSession(open: (String, String) -> Unit) { - open(StudeezDestinations.HOME_SCREEN, StudeezDestinations.SESSION_RECAP) + fun discardSession(open: (String) -> Unit) { + open(StudeezDestinations.HOME_SCREEN) } } \ No newline at end of file From 05e8951edff72431c8e78493ad3de3258f17ca3a Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 16:48:53 +0200 Subject: [PATCH 087/163] #109 refactor forms to be more consistent --- .../common/composable/FormComposable.kt | 22 +++++++++ .../subjects/form/SubjectFormScreen.kt | 4 +- .../screens/tasks/form/TaskFormScreen.kt | 4 +- .../{TimerFormRoute.kt => TimerFormScreen.kt} | 7 ++- .../form_screens/AbstractTimerFormScreen.kt | 48 ++++++------------- 5 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/common/composable/FormComposable.kt rename app/src/main/java/be/ugent/sel/studeez/screens/timer_form/{TimerFormRoute.kt => TimerFormScreen.kt} (87%) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/FormComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/FormComposable.kt new file mode 100644 index 0000000..1fbcfb2 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/FormComposable.kt @@ -0,0 +1,22 @@ +package be.ugent.sel.studeez.common.composable + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun FormComposable( + title: String, + popUp: () -> Unit, + content: @Composable () -> Unit, +) { + SecondaryScreenTemplate(title = title, popUp = popUp) { + Box( + modifier = Modifier.verticalScroll(rememberScrollState()), + ) { + content() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt index bd81c8c..26097ce 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.DeleteButton -import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.FormComposable import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.resources @@ -71,7 +71,7 @@ fun SubjectForm( onColorChange: (Color) -> Unit, extraButton: @Composable () -> Unit = {}, ) { - SecondaryScreenTemplate( + FormComposable( title = resources().getString(title), popUp = goBack, ) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt index 92302ea..79c744d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/tasks/form/TaskFormScreen.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.DeleteButton -import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.FormComposable import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.resources @@ -62,7 +62,7 @@ fun TaskForm( onNameChange: (String) -> Unit, extraButton: @Composable () -> Unit = {} ) { - SecondaryScreenTemplate( + FormComposable( title = resources().getString(title), popUp = goBack, ) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt index 0323dc2..542a7f0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormScreen.kt @@ -3,7 +3,7 @@ package be.ugent.sel.studeez.screens.timer_form import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.FormComposable import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.R.string as AppText @@ -36,7 +36,10 @@ fun TimerFormScreen( ) { val timerFormScreen = getTimerInfo().accept(GetTimerFormScreen()) - SecondaryScreenTemplate(title = stringResource(id = label), popUp = popUp) { + FormComposable( + title = stringResource(id = label), + popUp = popUp + ) { timerFormScreen(onConfirmClick) } } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt index 5f4a17b..69d02ef 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt @@ -1,17 +1,7 @@ package be.ugent.sel.studeez.screens.timer_form.form_screens -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.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.runtime.* import androidx.compose.ui.Modifier import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicButton @@ -32,32 +22,24 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { timerInfo.name = name timerInfo.description = description - Column( - verticalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxHeight().verticalScroll(rememberScrollState()), - ) { - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { + Column { - // Fields that every timer shares (ommited id) - LabelledInputField( - value = name, - onNewValue = { name = it }, - label = R.string.name - ) + // Fields that every timer shares (ommited id) + LabelledInputField( + value = name, + onNewValue = { name = it }, + label = R.string.name + ) - LabelledInputField( - value = description, - onNewValue = { description = it }, - label = AppText.description, - singleLine = false - ) + LabelledInputField( + value = description, + onNewValue = { description = it }, + label = AppText.description, + singleLine = false + ) - ExtraFields() + ExtraFields() - } BasicButton(R.string.save, Modifier.basicButton()) { onSaveClick(timerInfo) } From 4b09a8011c5aeb48cd83f0f40afb141305225297 Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 17:22:40 +0200 Subject: [PATCH 088/163] button to regenerate color --- app/build.gradle | 3 -- .../subjects/form/SubjectFormScreen.kt | 45 +++++++++++++++---- .../subjects/form/SubjectFormUiState.kt | 5 ++- .../subjects/form/SubjectFormViewModel.kt | 2 +- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a19cbd7..68d4e47 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -123,9 +123,6 @@ dependencies { implementation 'com.google.firebase:firebase-firestore-ktx' implementation 'com.google.firebase:firebase-perf-ktx' implementation 'com.google.firebase:firebase-config-ktx' - - // Colorpicker - implementation 'com.github.skydoves:colorpicker-compose:1.0.2' } // Allow references to generate code diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt index 19e6816..da59eda 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt @@ -1,20 +1,26 @@ package be.ugent.sel.studeez.screens.subjects.form import androidx.annotation.StringRes +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Column -import androidx.compose.material.OutlinedTextField +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme 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 androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.DeleteButton +import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.common.ext.fieldModifier +import be.ugent.sel.studeez.common.ext.generateRandomArgb import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.R.string as AppText @@ -31,7 +37,7 @@ fun SubjectCreateRoute( uiState = uiState, onConfirm = { viewModel.onCreate(openAndPopUp) }, onNameChange = viewModel::onNameChange, - onColorChange = {}, + onColorChange = viewModel::onColorChange, ) } @@ -48,7 +54,7 @@ fun SubjectEditRoute( uiState = uiState, onConfirm = { viewModel.onEdit(openAndPopUp) }, onNameChange = viewModel::onNameChange, - onColorChange = {}, + onColorChange = viewModel::onColorChange, ) { DeleteButton(text = AppText.delete_subject) { viewModel.onDelete(openAndPopUp) @@ -63,7 +69,7 @@ fun SubjectForm( uiState: SubjectFormUiState, onConfirm: () -> Unit, onNameChange: (String) -> Unit, - onColorChange: (Color) -> Unit, + onColorChange: (Long) -> Unit, extraButton: @Composable () -> Unit = {}, ) { SecondaryScreenTemplate( @@ -71,13 +77,13 @@ fun SubjectForm( popUp = goBack, ) { Column { - OutlinedTextField( + LabelledInputField( singleLine = true, value = uiState.name, - onValueChange = onNameChange, - placeholder = { Text(stringResource(id = AppText.name)) }, - modifier = Modifier.fieldModifier(), + onNewValue = onNameChange, + label = AppText.name, ) + ColorPicker(onColorChange, uiState) BasicButton( text = AppText.confirm, modifier = Modifier.basicButton(), @@ -88,6 +94,27 @@ fun SubjectForm( } } +@Composable +fun ColorPicker( + onColorChange: (Long) -> Unit, + uiState: SubjectFormUiState, +) { + Button( + onClick = { onColorChange(Color.generateRandomArgb()) }, + modifier = Modifier + .fieldModifier(), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color(uiState.color), +// contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f), + ), + shape = RoundedCornerShape(4.dp), +// border = BorderStroke(1.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)), + elevation = null, + ) { + Text(text = "Regenerate color") + } +} + @Preview @Composable fun AddSubjectFormPreview() { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt index 9fdba01..10a18e8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormUiState.kt @@ -1,6 +1,9 @@ package be.ugent.sel.studeez.screens.subjects.form +import androidx.compose.ui.graphics.Color +import be.ugent.sel.studeez.common.ext.generateRandomArgb + data class SubjectFormUiState( val name: String = "", - val color: Long = 0xFFFFD200, + val color: Long = Color.generateRandomArgb(), ) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt index eb7ee6f..7847a63 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormViewModel.kt @@ -46,7 +46,7 @@ class SubjectCreateFormViewModel @Inject constructor( fun onCreate(openAndPopUp: (String, String) -> Unit) { val newSubject = Subject( name = name, - argb_color = Color.generateRandomArgb(), + argb_color = color, ) subjectDAO.saveSubject( newSubject From 360fb122f73bce5f7bdc6442aed131a7de8c7437 Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 17:24:46 +0200 Subject: [PATCH 089/163] some code polishing --- .../sel/studeez/screens/subjects/form/SubjectFormScreen.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt index da59eda..d01061b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt @@ -101,15 +101,11 @@ fun ColorPicker( ) { Button( onClick = { onColorChange(Color.generateRandomArgb()) }, - modifier = Modifier - .fieldModifier(), + modifier = Modifier.fieldModifier(), colors = ButtonDefaults.buttonColors( backgroundColor = Color(uiState.color), -// contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f), ), shape = RoundedCornerShape(4.dp), -// border = BorderStroke(1.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)), - elevation = null, ) { Text(text = "Regenerate color") } From e8f2d71df3fb3c119ee30989fa8a5ce79ea8e2f2 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:25:51 +0200 Subject: [PATCH 090/163] deleteTimer Apptext --- .../be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt index 7cef001..cf8f374 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/TimerFormRoute.kt @@ -34,7 +34,7 @@ fun TimerEditRoute( @Composable fun deleteButton() { - DeleteButton(text = AppText.delete_subject) { + DeleteButton(text = AppText.delete_timer) { viewModel.deleteTimer(viewModel.getTimerInfo(), popUp) } } From eb28fa4ae40d9255347f5d2c409405d34ff67c26 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:26:19 +0200 Subject: [PATCH 091/163] valid and first booleans in map --- .../form_screens/AbstractTimerFormScreen.kt | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt index e936898..8ea31d2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt @@ -6,43 +6,37 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.LabeledErrorTextField -import be.ugent.sel.studeez.common.composable.LabelledInputField import be.ugent.sel.studeez.common.ext.basicButton +import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.R.string as AppText abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { + protected val valids = mutableMapOf( + "name" to mutableStateOf(textPredicate(timerInfo.name)), + "description" to mutableStateOf(textPredicate(timerInfo.description)) + ) + + protected val firsts = mutableMapOf( + "name" to mutableStateOf(true), + "description" to mutableStateOf(true) + ) + + @Composable operator fun invoke( onSaveClick: (TimerInfo) -> Unit, extraButton: @Composable () -> Unit ) { - - var name by remember { mutableStateOf(timerInfo.name) } - val isNameValid = remember { mutableStateOf(false) } - val hasEditedName = remember { mutableStateOf(true) } - - var description by remember { mutableStateOf(timerInfo.description) } - val isDescriptionValid = remember { mutableStateOf(false) } - val hasEditedDescription = remember { mutableStateOf(true) } - - // This shall rerun whenever name and description change - timerInfo.name = name - timerInfo.description = description - Column( verticalArrangement = Arrangement.SpaceBetween, modifier = Modifier @@ -59,25 +53,25 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { initialValue = timerInfo.name, label = R.string.name, errorText = AppText.name_error, - isValid = isNameValid, - isFirst = hasEditedName, + isValid = valids.getValue("name"), + isFirst = firsts.getValue("name"), keyboardType = KeyboardType.Text, predicate = { it.isNotBlank() } ) { correctName -> - name = correctName + timerInfo.name = correctName } LabeledErrorTextField( initialValue = timerInfo.description, label = R.string.description, errorText = AppText.description_error, - isValid = isDescriptionValid, - isFirst = hasEditedDescription, + isValid = valids.getValue("description"), + isFirst = firsts.getValue("description"), singleLine= false, keyboardType = KeyboardType.Text, - predicate = { it.isNotBlank() } + predicate = { textPredicate(it) } ) { correctName -> - description = correctName + timerInfo.description = correctName } ExtraFields() @@ -86,11 +80,11 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { Column { BasicButton(R.string.save, Modifier.basicButton()) { - if (isNameValid.value && isDescriptionValid.value) { + if (valids.all { it.component2().value }) { // All fields are valid onSaveClick(timerInfo) } else { - hasEditedName.value = false - hasEditedDescription.value = false + firsts.map { it.component2().value = false } // dont mask error because its not been filled out yet + SnackbarManager.showMessage(AppText.fill_out_error) } } extraButton() @@ -98,6 +92,10 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { } } + private fun textPredicate(text: String): Boolean { + return text.isNotBlank() + } + @Composable open fun ExtraFields() { // By default no extra fields, unless overwritten by subclass. From df0325074d04bbf042383d0653eb7e2125ca420a Mon Sep 17 00:00:00 2001 From: brreynie Date: Sat, 13 May 2023 17:29:25 +0200 Subject: [PATCH 092/163] i18n for regenerate button --- .../sel/studeez/screens/subjects/form/SubjectFormScreen.kt | 6 +++--- app/src/main/res/values/strings.xml | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt index d01061b..b89a851 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/subjects/form/SubjectFormScreen.kt @@ -1,17 +1,16 @@ package be.ugent.sel.studeez.screens.subjects.form import androidx.annotation.StringRes -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Column import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults -import androidx.compose.material.MaterialTheme 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 androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.composable.BasicButton @@ -104,10 +103,11 @@ fun ColorPicker( modifier = Modifier.fieldModifier(), colors = ButtonDefaults.buttonColors( backgroundColor = Color(uiState.color), + contentColor = Color.White, ), shape = RoundedCornerShape(4.dp), ) { - Text(text = "Regenerate color") + Text(text = stringResource(id = AppText.regenerate_color)) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70c4558..fa27f51 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,6 +46,7 @@ Delete Subject Delete Task View + Regenerate Color 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. From 2971ae85aa59b404cb313542c918c7693fc9203d Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:27:13 +0200 Subject: [PATCH 093/163] map implementation --- .../form_screens/BreakTimerFormScreen.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt index 333fc24..44ae76a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt @@ -15,6 +15,8 @@ class BreakTimerFormScreen( private val breakTimerInfo: PomodoroTimerInfo ): AbstractTimerFormScreen(breakTimerInfo) { + + @Composable override fun ExtraFields() { // If the user presses the OK button on the timepicker, the time in the button should change @@ -26,13 +28,17 @@ class BreakTimerFormScreen( breakTimerInfo.breakTime = newTime } + valids["repeats"] = remember {mutableStateOf(true)} + firsts["repeats"] = remember { mutableStateOf(true) } + LabeledErrorTextField( initialValue = breakTimerInfo.repeats.toString(), label = R.string.repeats, errorText = AppText.repeats_error, - isValid = mutableStateOf(false), + isValid = valids.getValue("repeats"), + isFirst = firsts.getValue("repeats"), keyboardType = KeyboardType.Decimal, - predicate = { it.matches(Regex("[1-9]+\\d*")) } + predicate = { isNumber(it) } ) { correctlyTypedInt -> breakTimerInfo.repeats = correctlyTypedInt.toInt() } @@ -40,6 +46,10 @@ class BreakTimerFormScreen( } } +fun isNumber(text: String): Boolean { + return text.matches(Regex("[1-9]+\\d*")) +} + @Preview @Composable fun BreakEditScreenPreview() { From 1330396ba8f7e9294157f34bdf16d0a0d33927c7 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:38:04 +0200 Subject: [PATCH 094/163] made buttons fill out screen --- .../timer_type_select/TimerTypeSelectScreen.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt index fa8d650..4825c63 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/timer_type_select/TimerTypeSelectScreen.kt @@ -1,13 +1,13 @@ package be.ugent.sel.studeez.screens.timer_form.timer_type_select -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.* import androidx.compose.material.Button 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.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.data.local.models.timer_info.* @@ -37,7 +37,10 @@ fun TimerTypeSelectScreen( ) { TimerType.values().forEach { timerType -> val default: TimerInfo = defaultTimerInfo.getValue(timerType) - Button(onClick = { viewModel.onTimerTypeChosen(default, open) }) { + Button( + onClick = { viewModel.onTimerTypeChosen(default, open) }, + modifier = Modifier.fillMaxWidth().padding(5.dp) + ) { Text(text = timerType.name) } } From eef4a8caf5dfe979754125d2c44efb7f72bda479 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Sat, 13 May 2023 17:38:29 +0200 Subject: [PATCH 095/163] added fillout error text --- app/src/main/res/values/strings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70c4558..303fa29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,8 +69,15 @@ Timers + Delete Timer Edit Add timer + + Name should not be blank + Description should not be blank + Fill out all the fields correctly! + + Select time Focus! From 75411627ac2b62699653b9597b2ca8079f191327 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:06:37 +0200 Subject: [PATCH 096/163] #71 Define Friendship DAO's --- .../studeez/data/local/models/Friendship.kt | 11 +++++ .../ugent/sel/studeez/domain/FriendshipDAO.kt | 48 +++++++++++++++++++ .../be/ugent/sel/studeez/domain/UserDAO.kt | 33 +++++++++++-- 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt new file mode 100644 index 0000000..98aa9a5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/Friendship.kt @@ -0,0 +1,11 @@ +package be.ugent.sel.studeez.data.local.models + +import com.google.firebase.Timestamp +import com.google.firebase.firestore.DocumentId + +data class Friendship( + @DocumentId val id: String = "", + val friendId: String = "", + val friendsSince: Timestamp = Timestamp.now(), + val accepted: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt new file mode 100644 index 0000000..eda933d --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt @@ -0,0 +1,48 @@ +package be.ugent.sel.studeez.domain + +import be.ugent.sel.studeez.data.local.models.Friendship +import kotlinx.coroutines.flow.Flow + +/** + * Should be used for interactions between friends. + */ +interface FriendshipDAO { + + /** + * @return all friendships of the user that is currently logged in. + */ + fun getAllFriendships(): Flow> + + /** + * @return the amount of friends of the currently logged in user. + * This method should be faster than just counting the length of getAllFriends() + */ + fun getFriendshipCount(): Flow + + /** + * @param id the id of the friendship that you want details of + * @return the details of a Friendship + */ + fun getFriendshipDetails(id: String): Friendship + + /** + * Send a friend request to a user. + * @param id of the user that you want to add as a friend + * @return Success/faillure of transaction + */ + fun sendFriendshipRequest(id: String): Boolean + + /** + * Accept a friend request that has already been sent. + * @param id of the friendship that you want to update + * @return: Success/faillure of transaction + */ + fun acceptFriendship(id: String): Boolean + + /** + * Remove a friend or decline a friendrequest. + * @param id of the friendship that you want to update + * @return: Success/faillure of transaction + */ + fun removeFriendship(id: String): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index 619c77b..6017342 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -1,19 +1,44 @@ package be.ugent.sel.studeez.domain import be.ugent.sel.studeez.data.local.models.User +import kotlinx.coroutines.flow.Flow interface UserDAO { - suspend fun getUser(): User + /** + * @return all users + */ + fun getAllUsers(): Flow> - suspend fun saveUser( + /** + * @return all users based on a query, a trimmed down version of getAllUsers() + */ + fun getUsersWithQuery(): Flow> + // TODO Add query parameter + + /** + * Request information about a user + */ + fun getUserDetails( + userId: String + ): Flow + + /** + * @return information on the currently logged in user. + */ + suspend fun getLoggedInUser(): User + // TODO Should be refactored to fun getLoggedInUser(): Flow, without suspend. + + suspend fun saveLoggedInUser( newUsername: String, newBiography: String = "" ) + // TODO Should be refactored to fun saveLoggedInUser(...): Boolean, without suspend. /** - * Delete all references to this user in the database. Similar to the deleteCascade in + * Delete all references to the logged in user in the database. Similar to the deleteCascade in * relational databases. */ - suspend fun deleteUserReferences() + suspend fun deleteLoggedInUserReferences() + // TODO Should be refactored to fun deleteLoggedInUserReferences(): Boolean, without suspend. } \ No newline at end of file From 56427a69af2cdd07de10c1435b71c58bb87e7103 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:30:36 +0200 Subject: [PATCH 097/163] Allow build --- .../be/ugent/sel/studeez/di/DatabaseModule.kt | 3 ++ .../implementation/FirebaseCollections.kt | 1 + .../domain/implementation/FirebaseUserDAO.kt | 39 +++++++++++++------ .../screens/profile/ProfileEditViewModel.kt | 6 +-- .../screens/profile/ProfileViewModel.kt | 4 +- .../screens/sign_up/SignUpViewModel.kt | 2 +- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index 93dea2d..a24adc2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -16,6 +16,9 @@ abstract class DatabaseModule { @Binds abstract fun provideUserDAO(impl: FirebaseUserDAO): UserDAO + @Binds + abstract fun provideFriendshipDAO(impl: FirebaseFriendshipDAO): FriendshipDAO + @Binds abstract fun provideTimerDAO(impl: FirebaseTimerDAO): TimerDAO diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt index fcdc0a4..042c0f0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseCollections.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.domain.implementation object FirebaseCollections { const val SESSION_COLLECTION = "sessions" const val USER_COLLECTION = "users" + const val FRIENDS_COLLECTION = "friends" const val TIMER_COLLECTION = "timers" const val SUBJECT_COLLECTION = "subjects" const val TASK_COLLECTION = "tasks" diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index b9421de..293dd1f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -7,6 +7,7 @@ import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -15,7 +16,30 @@ class FirebaseUserDAO @Inject constructor( private val auth: AccountDAO ) : UserDAO { - override suspend fun getUser(): User { + companion object { + private const val USER_COLLECTION = FirebaseCollections.USER_COLLECTION + private const val USERNAME_FIELD = "username" + private const val BIOGRAPHY_FIELD = "biography" + } + + private fun currentUserDocument(): DocumentReference = + firestore + .collection(USER_COLLECTION) + .document(auth.currentUserId) + + override fun getAllUsers(): Flow> { + TODO("Not yet implemented") + } + + override fun getUsersWithQuery(): Flow> { + TODO("Not yet implemented") + } + + override fun getUserDetails(userId: String): Flow { + TODO("Not yet implemented") + } + + override suspend fun getLoggedInUser(): User { val userDocument = currentUserDocument().get().await() return User( username = userDocument.getString(USERNAME_FIELD) ?: "", @@ -23,7 +47,7 @@ class FirebaseUserDAO @Inject constructor( ) } - override suspend fun saveUser( + override suspend fun saveLoggedInUser( newUsername: String, newBiography: String ) { @@ -33,16 +57,7 @@ class FirebaseUserDAO @Inject constructor( )) } - private fun currentUserDocument(): DocumentReference = - firestore.collection(USER_COLLECTION).document(auth.currentUserId) - - companion object { - private const val USER_COLLECTION = "users" - private const val USERNAME_FIELD = "username" - private const val BIOGRAPHY_FIELD = "biography" - } - - override suspend fun deleteUserReferences() { + override suspend fun deleteLoggedInUserReferences() { currentUserDocument().delete() .addOnSuccessListener { SnackbarManager.showMessage(R.string.success) } .addOnFailureListener { SnackbarManager.showMessage(R.string.generic_error) } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt index d715e10..9370881 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt @@ -24,7 +24,7 @@ class ProfileEditViewModel @Inject constructor( init { launchCatching { - val user: User = userDAO.getUser() + val user: User = userDAO.getLoggedInUser() uiState.value = uiState.value.copy( username = user.username, biography = user.biography @@ -42,7 +42,7 @@ class ProfileEditViewModel @Inject constructor( fun onSaveClick() { launchCatching { - userDAO.saveUser( + userDAO.saveLoggedInUser( newUsername = uiState.value.username, newBiography = uiState.value.biography ) @@ -52,7 +52,7 @@ class ProfileEditViewModel @Inject constructor( fun onDeleteClick(openAndPopUp: (String, String) -> Unit) { launchCatching { - userDAO.deleteUserReferences() // Delete references + userDAO.deleteLoggedInUserReferences() // Delete references accountDAO.deleteAccount() // Delete authentication } openAndPopUp(StudeezDestinations.SIGN_UP_SCREEN, StudeezDestinations.EDIT_PROFILE_SCREEN) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index 760b962..d1fba29 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -14,11 +14,11 @@ class ProfileViewModel @Inject constructor( ) : StudeezViewModel(logService) { suspend fun getUsername(): String { - return userDAO.getUser().username + return userDAO.getLoggedInUser().username } suspend fun getBiography(): String { - return userDAO.getUser().biography + return userDAO.getLoggedInUser().biography } fun onEditProfileClick(open: (String) -> Unit) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt index 2a448c8..4cfa6a9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt @@ -66,7 +66,7 @@ class SignUpViewModel @Inject constructor( launchCatching { accountDAO.signUpWithEmailAndPassword(email, password) accountDAO.signInWithEmailAndPassword(email, password) - userDAO.saveUser(username) + userDAO.saveLoggedInUser(username) openAndPopUp(HOME_SCREEN, SIGN_UP_SCREEN) } } From 4f08453a2dbe057f6672489e036770d84b9b5eb4 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:36:18 +0200 Subject: [PATCH 098/163] Add getAllFriendships() --- .../implementation/FirebaseFriendshipDAO.kt | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt new file mode 100644 index 0000000..4f2203c --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -0,0 +1,49 @@ +package be.ugent.sel.studeez.domain.implementation + +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.domain.AccountDAO +import be.ugent.sel.studeez.domain.FriendshipDAO +import com.google.firebase.firestore.DocumentReference +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 FirebaseFriendshipDAO @Inject constructor( + private val firestore: FirebaseFirestore, + private val auth: AccountDAO +): FriendshipDAO { + + private fun currentUserDocument(): DocumentReference = firestore + .collection(FirebaseCollections.USER_COLLECTION) + .document(auth.currentUserId) + + override fun getAllFriendships(): Flow> { + return currentUserDocument() + .collection(FirebaseCollections.FRIENDS_COLLECTION) + .snapshots() + .map { it.toObjects(Friendship::class.java) } + } + + override fun getFriendshipCount(): Flow { + TODO("Not yet implemented") + } + + override fun getFriendshipDetails(id: String): Friendship { + TODO("Not yet implemented") + } + + override fun sendFriendshipRequest(id: String): Boolean { + TODO("Not yet implemented") + } + + override fun acceptFriendship(id: String): Boolean { + TODO("Not yet implemented") + } + + override fun removeFriendship(id: String): Boolean { + TODO("Not yet implemented") + } + +} \ No newline at end of file From a6d4cf1369a4878516d80128bd276c1ce8d9a09f Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:36:56 +0200 Subject: [PATCH 099/163] add getAllUsers() --- .../sel/studeez/domain/implementation/FirebaseUserDAO.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 293dd1f..df592fe 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -7,7 +7,9 @@ import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -28,7 +30,10 @@ class FirebaseUserDAO @Inject constructor( .document(auth.currentUserId) override fun getAllUsers(): Flow> { - TODO("Not yet implemented") + return firestore + .collection(FirebaseCollections.USER_COLLECTION) + .snapshots() + .map { it.toObjects(User::class.java) } } override fun getUsersWithQuery(): Flow> { From bb64875bad5512b1f3f9861885e6c07ad9c84bb8 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 09:39:34 +0200 Subject: [PATCH 100/163] Add getUserDetails() --- .../studeez/domain/implementation/FirebaseUserDAO.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index df592fe..8c84914 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -9,6 +9,7 @@ import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.tasks.await import javax.inject.Inject @@ -41,7 +42,15 @@ class FirebaseUserDAO @Inject constructor( } override fun getUserDetails(userId: String): Flow { - TODO("Not yet implemented") + return flow { + val snapshot = firestore + .collection(FirebaseCollections.USER_COLLECTION) + .document(userId) + .get() + .await() + val user = snapshot.toObject(User::class.java)!! + emit(user) + } } override suspend fun getLoggedInUser(): User { From 3b50054ff5ef182a6b1e54574e34a781a0c27782 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 12:07:56 +0200 Subject: [PATCH 101/163] #63 Add getFriendshipCount and add to profile screen --- .../common/composable/ButtonComposable.kt | 6 +- .../implementation/FirebaseFriendshipDAO.kt | 24 ++++++- .../studeez/navigation/StudeezDestinations.kt | 1 + .../sel/studeez/navigation/StudeezNavGraph.kt | 4 ++ .../studeez/screens/profile/ProfileScreen.kt | 68 +++++++++++++++++-- .../screens/profile/ProfileViewModel.kt | 11 +++ app/src/main/res/values/strings.xml | 6 +- 7 files changed, 111 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt index 73ae1b5..2670095 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt @@ -31,7 +31,11 @@ import be.ugent.sel.studeez.common.ext.defaultButtonShape import be.ugent.sel.studeez.R.string as AppText @Composable -fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) { +fun BasicTextButton( + @StringRes text: Int, + modifier: Modifier, + action: () -> Unit +) { TextButton( onClick = action, modifier = modifier diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt index 4f2203c..4665e0f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -1,5 +1,6 @@ package be.ugent.sel.studeez.domain.implementation +import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.FriendshipDAO @@ -7,8 +8,14 @@ import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import be.ugent.sel.studeez.R.string as AppText class FirebaseFriendshipDAO @Inject constructor( private val firestore: FirebaseFirestore, @@ -27,7 +34,22 @@ class FirebaseFriendshipDAO @Inject constructor( } override fun getFriendshipCount(): Flow { - TODO("Not yet implemented") + return flow { + val friendshipCount = suspendCoroutine { continuation -> + currentUserDocument() + .collection(FirebaseCollections.FRIENDS_COLLECTION) + .get() + .addOnSuccessListener { querySnapshot -> + continuation.resume(querySnapshot.size()) + } + .addOnFailureListener { exception -> + continuation.resumeWithException(exception) + } + } + emit(friendshipCount) + }.catch { + SnackbarManager.showMessage(AppText.generic_error) + } } override fun getFriendshipDetails(id: String): Friendship { diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index 49856c9..2df3899 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -30,6 +30,7 @@ object StudeezDestinations { const val EDIT_TASK_FORM = "edit_task" // Friends flow + const val FRIENDS_OVERVIEW_SCREEN = "friends_overview" const val SEARCH_FRIENDS_SCREEN = "search_friends" // Create & edit screens diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index a09846a..0c4b803 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -220,6 +220,10 @@ fun StudeezNavGraph( } // Friends flow + composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) { + // TODO + } + composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { // TODO } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt index 3652c70..ca59fba 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt @@ -1,30 +1,38 @@ package be.ugent.sel.studeez.screens.profile -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.Button import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign 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.Headline 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.ext.defaultButtonShape import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow import be.ugent.sel.studeez.R.string as AppText data class ProfileActions( val getUsername: suspend CoroutineScope.() -> String?, val getBiography: suspend CoroutineScope.() -> String?, - val onEditProfileClick: () -> Unit + val getAmountOfFriends: () -> Flow, + val onEditProfileClick: () -> Unit, + val onViewFriendsClick: () -> Unit ) fun getProfileActions( @@ -34,7 +42,9 @@ fun getProfileActions( return ProfileActions( getUsername = { viewModel.getUsername() }, getBiography = { viewModel.getBiography() }, - onEditProfileClick = { viewModel.onEditProfileClick(open) } + getAmountOfFriends = { viewModel.getAmountOfFriends() }, + onEditProfileClick = { viewModel.onEditProfileClick(open) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) } ) } @@ -60,6 +70,8 @@ fun ProfileScreen( ) { var username: String? by remember { mutableStateOf("") } var biography: String? by remember { mutableStateOf("") } + val amountOfFriends = profileActions.getAmountOfFriends().collectAsState(initial = 0) + LaunchedEffect(key1 = Unit) { username = profileActions.getUsername(this) biography = profileActions.getBiography(this) @@ -76,6 +88,21 @@ fun ProfileScreen( item { Headline(text = username ?: resources().getString(AppText.no_username)) } + + item { + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + modifier = Modifier.fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally) + ) { + AmountOfFriendsButton( + amountOfFriends = amountOfFriends.value + ) { + profileActions.onViewFriendsClick() + } + } + } + item { Text( text = biography ?: "", @@ -96,7 +123,6 @@ fun EditAction( imageVector = Icons.Default.Edit, contentDescription = resources().getString(AppText.edit_profile) ) - } } @@ -104,8 +130,38 @@ fun EditAction( @Composable fun ProfileScreenPreview() { ProfileScreen( - profileActions = ProfileActions({ null }, { null }, {}), + profileActions = ProfileActions({ null }, { null }, { emptyFlow() }, {}, {}), drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) +} + +@Composable +fun AmountOfFriendsButton( + amountOfFriends: Int, + onClick: () -> Unit +){ + Button( + onClick = onClick, + shape = defaultButtonShape() + ) { + Text( + text = resources().getQuantityString( + /* id = */ R.plurals.friends_amount, + /* quantity = */ amountOfFriends, + /* ...formatArgs = */ amountOfFriends + ) + ) + } +} + +@Preview +@Composable +fun AmountOfFriendsButtonPreview() { + StudeezTheme { + Column { + AmountOfFriendsButton(amountOfFriends = 1) { } + AmountOfFriendsButton(amountOfFriends = 100) { } + } + } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index d1fba29..e485de6 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -1,15 +1,18 @@ package be.ugent.sel.studeez.screens.profile +import be.ugent.sel.studeez.domain.FriendshipDAO import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO 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 ProfileViewModel @Inject constructor( private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, logService: LogService ) : StudeezViewModel(logService) { @@ -21,8 +24,16 @@ class ProfileViewModel @Inject constructor( return userDAO.getLoggedInUser().biography } + fun getAmountOfFriends(): Flow { + return friendshipDAO.getFriendshipCount() + } + fun onEditProfileClick(open: (String) -> Unit) { open(StudeezDestinations.EDIT_PROFILE_SCREEN) } + fun onViewFriendsClick(open: (String) -> Unit) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } + } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ed2693..f370b35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,7 +20,7 @@ Success! Try again - Something wrong happened. Please try again. + Something went wrong. Please try again. Please insert a valid email. @@ -107,6 +107,10 @@ Friends Friend + + %d Friend + %d Friends + Adding friends still needs to be implemented. Hang on tight! From 87fe4767245571119e97c98ec2020a23c481b831 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Sun, 14 May 2023 13:10:32 +0200 Subject: [PATCH 102/163] Basic friends overview screen --- .../screens/friend/FriendsOverviewScreen.kt | 218 ++++++++++++++++++ .../friend/FriendsOverviewViewModel.kt | 41 ++++ .../main/res/drawable/ic_more_horizontal.xml | 5 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 266 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt create mode 100644 app/src/main/res/drawable/ic_more_horizontal.xml diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt new file mode 100644 index 0000000..c9e80f5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt @@ -0,0 +1,218 @@ +package be.ugent.sel.studeez.screens.friend + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.common.composable.BasicButton +import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.ext.basicButton +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import com.google.firebase.Timestamp +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import be.ugent.sel.studeez.R.string as AppText + +data class FriendsOverviewActions( + val getFriendsFlow: () -> Flow>>, + val searchFriends: () -> Unit +) + +fun getFriendsOverviewActions( + viewModel: FriendsOverviewViewModel, + open: (String) -> Unit +): FriendsOverviewActions { + return FriendsOverviewActions( + getFriendsFlow = viewModel::getAllFriends, + searchFriends = { viewModel.searchFriends(open) } + ) +} + +@Composable +fun FriendsOveriewRoute( + open: (String) -> Unit, + popUp: () -> Unit, + viewModel: FriendsOverviewViewModel +) { + FriendsOverviewScreen( + friendsOverviewActions = getFriendsOverviewActions( + viewModel = viewModel, + open = open + ), + popUp = popUp + ) +} + +@Composable +fun FriendsOverviewScreen( + friendsOverviewActions: FriendsOverviewActions, + popUp: () -> Unit +) { + val friends = friendsOverviewActions.getFriendsFlow().collectAsState(initial = emptyList()) + + SecondaryScreenTemplate( + title = "TODO there needs to be a search field here", // TODO + popUp = popUp + ) { + LazyColumn { + if (friends.value.isEmpty()) { + // Show a quick button to search friends when the user does not have any friends yet. + item { + BasicButton( + text = AppText.no_friends, + modifier = Modifier.basicButton() + ) { + friendsOverviewActions.searchFriends() + } + } + } + + items(friends.value) { friend -> + FriendsEntry( + user = friend.first, + friendship = friend.second + ) + } + } + } +} + +@Preview +@Composable +fun FriendsOverviewPreview() { + StudeezTheme { + FriendsOverviewScreen( + friendsOverviewActions = FriendsOverviewActions( + getFriendsFlow = { emptyFlow() }, + searchFriends = {} + ), + popUp = {} + ) + } +} + +@Composable +fun FriendsEntry( + user: User, + friendship: Friendship +) { + // TODO Styling + Row ( + modifier = Modifier + .fillMaxWidth() + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.15f) + .background(MaterialTheme.colors.primary, CircleShape) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_visibility_on), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .align(Alignment.Center), + tint = MaterialTheme.colors.onPrimary + ) + } + + Box ( + modifier = Modifier + .fillMaxWidth(0.65f) + ) { + Column ( + modifier = Modifier + .padding(vertical = 4.dp) + ) { + Text( + text = user.username, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "${resources().getString(AppText.app_name)} ${resources().getString(AppText.friend)}", + fontSize = 14.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Box( + modifier = Modifier.fillMaxWidth(0.15f) + ) { + ThreeDots(friendship = friendship) + } + } +} + +@Preview +@Composable +fun FriendsEntryPreview() { + StudeezTheme { + FriendsEntry( + user = User( + id = "", + username = "Tibo De Peuter", + biography = "short bio" + ), + friendship = Friendship( + id = "", + friendId = "someId", + friendsSince = Timestamp.now(), + accepted = true + ) + ) + } +} + +@Composable +fun ThreeDots( + friendship: Friendship +) { + IconButton( + onClick = { /* TODO Open dropdown */ } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = resources().getString(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } +} + +@Preview +@Composable +fun ThreeDotsPreview() { + StudeezTheme { + ThreeDots( + friendship = Friendship( + id = "", + friendId = "someId", + friendsSince = Timestamp.now(), + accepted = true + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt new file mode 100644 index 0000000..0dec506 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt @@ -0,0 +1,41 @@ +package be.ugent.sel.studeez.screens.friend + +import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +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 kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapConcat +import javax.inject.Inject + +@HiltViewModel +class FriendsOverviewViewModel @Inject constructor( + private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, + logService: LogService +) : StudeezViewModel(logService) { + + fun getAllFriends(): Flow>> { + return friendshipDAO.getAllFriendships() + .flatMapConcat { friendships -> + val userFlows = friendships.map { friendship -> + userDAO.getUserDetails(friendship.friendId) + } + combine(userFlows) { users -> + friendships.zip(users) { friendship, user -> + Pair(user, friendship) + } + } + } + } + + fun searchFriends(open: (String) -> Unit) { + open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_more_horizontal.xml b/app/src/main/res/drawable/ic_more_horizontal.xml new file mode 100644 index 0000000..afbe22d --- /dev/null +++ b/app/src/main/res/drawable/ic_more_horizontal.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f370b35..1ae1bc8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ Go back Next Start + View more Success! @@ -112,6 +113,7 @@ %d Friends Adding friends still needs to be implemented. Hang on tight! + You don\'t have any friends yet. Add one! From 9d13a3395955dd933ace60e43a9552190d5d7079 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:02:56 +0200 Subject: [PATCH 103/163] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/AbstractTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt index 8ea31d2..8afe306 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/AbstractTimerFormScreen.kt @@ -34,7 +34,7 @@ abstract class AbstractTimerFormScreen(private val timerInfo: TimerInfo) { @Composable operator fun invoke( onSaveClick: (TimerInfo) -> Unit, - extraButton: @Composable () -> Unit + extraButton: @Composable () -> Unit = {}, ) { Column( From 29b976cae426d2a1879ed5bd0621dd13e6adf475 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:03:21 +0200 Subject: [PATCH 104/163] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/BreakTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt index 44ae76a..c87bd7b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/BreakTimerFormScreen.kt @@ -61,6 +61,6 @@ fun BreakEditScreenPreview() { 5 ) StudeezTheme { - BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}, {}) + BreakTimerFormScreen(pomodoroTimerInfo).invoke(onSaveClick = {}) } } \ No newline at end of file From f722218abe0f8e8de0113c62478ff271ab3a440c Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:03:29 +0200 Subject: [PATCH 105/163] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/CustomTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt index b0000a0..27c0657 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/CustomTimerFormScreen.kt @@ -29,6 +29,6 @@ class CustomTimerFormScreen( fun CustomEditScreenPreview() { val customTimerInfo = CustomTimerInfo("custom", "my description", 25) StudeezTheme { - CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}, extraButton = {}) + CustomTimerFormScreen(customTimerInfo).invoke(onSaveClick = {}) } } \ No newline at end of file From bd1c4df3012a53f25a360dbb7681e4554225188f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 11:03:37 +0200 Subject: [PATCH 106/163] Update app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt Co-authored-by: brreynie --- .../screens/timer_form/form_screens/EndlessTimerFormScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt index e096946..9009fff 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_form/form_screens/EndlessTimerFormScreen.kt @@ -18,6 +18,6 @@ fun EndlessEditScreenPreview() { "My endless timer description", ) StudeezTheme { - EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}, {}) + EndlessTimerFormScreen(endlessTimerInfo).invoke(onSaveClick = {}) } } \ No newline at end of file From 6938b3e868ca5ebd48ff45d576054098dd0811a3 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 12:52:57 +0200 Subject: [PATCH 107/163] #118 AbstractSessionScreen -> this file --- .../composables/BreakTimerScreenComposable.kt | 2 + .../CustomTimerSessionScreenComposable.kt | 2 + .../EndlessTimerSessionScreenComposable.kt | 4 ++ .../composables/SessionScreenComposable.kt | 67 +++++++++++++++++++ .../composables/TimerComposable.kt | 2 + 5 files changed, 77 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt new file mode 100644 index 0000000..da72fe0 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -0,0 +1,2 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt new file mode 100644 index 0000000..da72fe0 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -0,0 +1,2 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt new file mode 100644 index 0000000..683b284 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + +class EndlessTimerSessionScreenComposable { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt new file mode 100644 index 0000000..d148e19 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -0,0 +1,67 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer +import be.ugent.sel.studeez.screens.session.SessionActions + +@Composable +fun SessionScreen( + open: (String) -> Unit, + sessionActions: SessionActions, + motivationString: @Composable () -> String +) { + Column( + modifier = Modifier.padding(10.dp) + ) { + Timer(sessionActions = sessionActions, motivationString = motivationString) + Box( + contentAlignment = Alignment.Center, modifier = Modifier + .fillMaxWidth() + .padding(50.dp) + ) { + EndSessionButton(sessionActions = sessionActions) + } + } +} + +@Composable +fun EndSessionButton(sessionActions: SessionActions) { + TextButton( + onClick = { + sessionActions.releaseMediaPlayer + sessionActions.endSession() + }, + modifier = Modifier + .padding(horizontal = 20.dp) + .border(1.dp, Color.Red, RoundedCornerShape(32.dp)) + .background(Color.Transparent) + ) { + EndsessionText() + } +} + +@Composable +fun EndsessionText() { + Text( + text = "End session", + color = Color.Red, + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + modifier = Modifier.padding(1.dp) + ) +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt new file mode 100644 index 0000000..ba3f71d --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -0,0 +1,2 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens + From 5073e5cb224c8a77d0d67a3309558403dd7aa9cf Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 12:54:07 +0200 Subject: [PATCH 108/163] #118 subclass of AbstractSessionScreen -> this file --- .../composables/BreakTimerScreenComposable.kt | 36 +++++++++++++++++++ .../CustomTimerSessionScreenComposable.kt | 27 ++++++++++++++ .../EndlessTimerSessionScreenComposable.kt | 22 +++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index da72fe0..11cce8d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -1,2 +1,38 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables +import androidx.compose.runtime.Composable +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.screens.session.SessionActions + +@Composable +fun BreakSessionScreenComposable( + open: (String) -> Unit, + sessionActions: SessionActions, + pomodoroTimer: FunctionalPomodoroTimer +) { + SessionScreen( + open = open, + sessionActions = sessionActions + ) { + motivationString(pomodoroTimer = pomodoroTimer) + } +} + +@Composable +private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { + if (pomodoroTimer.isInBreak) { + return resources().getString(R.string.state_take_a_break) + } + + if (pomodoroTimer.hasEnded()) { + return resources().getString(R.string.state_done) + } + + return resources().getQuantityString( + R.plurals.state_focus_remaining, + pomodoroTimer.breaksRemaining, + pomodoroTimer.breaksRemaining + ) +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt index da72fe0..bafcb19 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -1,2 +1,29 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables +import androidx.compose.runtime.Composable +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.screens.session.SessionActions + +@Composable +fun CustomTimerSessionScreenComposable( + open: (String) -> Unit, + sessionActions: SessionActions, + customTimer: FunctionalCustomTimer +) { + SessionScreen( + open = open, + sessionActions = sessionActions + ) { + motivationString(customTimer = customTimer) + } +} + +@Composable +private fun motivationString(customTimer: FunctionalCustomTimer): String { + if (customTimer.hasEnded()) { + return resources().getString(R.string.state_done) + } + return resources().getString(R.string.state_focus) +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt index 683b284..b223f52 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt @@ -1,4 +1,24 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables -class EndlessTimerSessionScreenComposable { +import androidx.compose.runtime.Composable +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.screens.session.SessionActions + +@Composable +fun EndlessTimerSessionScreenComposable( + open: (String) -> Unit, + sessionActions: SessionActions, +) { + SessionScreen( + open = open, + sessionActions = sessionActions + ) { + motivationString() + } +} + +@Composable +private fun motivationString(): String { + return resources().getString(R.string.state_focus) } \ No newline at end of file From fdd0429e320b69d9e5ff9648cb4be898ce4e0ef4 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 12:54:37 +0200 Subject: [PATCH 109/163] TimerComposable als apparte file --- .../composables/TimerComposable.kt | 92 ++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index ba3f71d..6123a8a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -1,2 +1,92 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens +package be.ugent.sel.studeez.screens.session.sessionScreens.composables +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds +import be.ugent.sel.studeez.screens.session.SessionActions +import kotlinx.coroutines.delay +import kotlin.time.Duration.Companion.seconds + +@Composable +fun Timer( + sessionActions: SessionActions, + motivationString: @Composable () -> String +) { + var tikker by remember { mutableStateOf(false) } + LaunchedEffect(tikker) { + delay(1.seconds) + sessionActions.getTimer().tick() + // callMediaPlayer() TODO + tikker = !tikker + } + + val hms = sessionActions.getTimer().getHoursMinutesSeconds() + Column { + + TimerClock(hms) + MotivationText(text = motivationString()) + + Box( + contentAlignment = Alignment.Center, modifier = Modifier + .fillMaxWidth() + .padding(50.dp) + ) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .padding(16.dp) + .background(Color.Blue, RoundedCornerShape(32.dp)) + ) { + TaskText(taskName = sessionActions.getTask()) + } + } + } +} + +@Composable +fun TimerClock(hms: HoursMinutesSeconds) { + Text( + text = hms.toString(), + modifier = Modifier + .fillMaxWidth() + .padding(50.dp), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + fontSize = 40.sp, + ) +} + +@Composable +fun MotivationText(text: String) { + Text( + text = text, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Light, + fontSize = 30.sp + ) +} + +@Composable +fun TaskText(taskName: String) { + Text( + text = taskName, + color = Color.White, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(vertical = 4.dp, horizontal = 20.dp) + ) +} \ No newline at end of file From 492775565c014b131f8821ec5220198f35b0b80e Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 13:23:11 +0200 Subject: [PATCH 110/163] #118 integrated dots in composition --- .../composables/BreakTimerScreenComposable.kt | 39 ++++++++++++++++++- .../composables/SessionScreenComposable.kt | 10 ++++- .../composables/TimerComposable.kt | 4 +- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index 11cce8d..b548591 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -1,6 +1,14 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape 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.unit.dp import be.ugent.sel.studeez.R import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.resources @@ -14,12 +22,39 @@ fun BreakSessionScreenComposable( ) { SessionScreen( open = open, - sessionActions = sessionActions + sessionActions = sessionActions, + midSection = { Dots(pomodoroTimer) }, + motivationString = { motivationString(pomodoroTimer = pomodoroTimer) } + ) +} + +@Composable +private fun Dots(pomodoroTimer: FunctionalPomodoroTimer) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, ) { - motivationString(pomodoroTimer = pomodoroTimer) + repeat(pomodoroTimer.repeats - pomodoroTimer.breaksRemaining) { + Dot(color = Color.DarkGray) + } + if (!pomodoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) + repeat(pomodoroTimer.breaksRemaining - 1) { + Dot(color = Color.Gray) + } } } +@Composable +private fun Dot(color: Color) { + Box(modifier = Modifier + .padding(5.dp) + .size(10.dp) + .clip(CircleShape) + .background(color)) +} + + @Composable private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { if (pomodoroTimer.isInBreak) { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt index d148e19..5ed29f8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -23,12 +23,18 @@ import be.ugent.sel.studeez.screens.session.SessionActions fun SessionScreen( open: (String) -> Unit, sessionActions: SessionActions, - motivationString: @Composable () -> String + midSection: @Composable () -> Unit = {}, + motivationString: @Composable () -> String, + ) { Column( modifier = Modifier.padding(10.dp) ) { - Timer(sessionActions = sessionActions, motivationString = motivationString) + Timer( + sessionActions = sessionActions, + motivationString = motivationString, + midSection = midSection + ) Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index 6123a8a..3b65599 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -23,7 +23,8 @@ import kotlin.time.Duration.Companion.seconds @Composable fun Timer( sessionActions: SessionActions, - motivationString: @Composable () -> String + motivationString: @Composable () -> String, + midSection: @Composable () -> Unit ) { var tikker by remember { mutableStateOf(false) } LaunchedEffect(tikker) { @@ -39,6 +40,7 @@ fun Timer( TimerClock(hms) MotivationText(text = motivationString()) + Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() From 71b9550bd04546aa87726b89faaebae9da66a238 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:08:54 +0200 Subject: [PATCH 111/163] Move profile edit screen to new package --- .../screens/profile/{ => edit_profile}/ProfileEditScreen.kt | 2 +- .../screens/profile/{ => edit_profile}/ProfileEditUiState.kt | 2 +- .../screens/profile/{ => edit_profile}/ProfileEditViewModel.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditScreen.kt (98%) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditUiState.kt (62%) rename app/src/main/java/be/ugent/sel/studeez/screens/profile/{ => edit_profile}/ProfileEditViewModel.kt (97%) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt similarity index 98% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt index 950595f..31dcb9d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditScreen.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt similarity index 62% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt index c686c92..911df68 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditUiState.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile data class ProfileEditUiState ( val username: String = "", diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt similarity index 97% rename from app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt index 9370881..57bbbc0 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/edit_profile/ProfileEditViewModel.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.profile +package be.ugent.sel.studeez.screens.profile.edit_profile import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.R From bc7753fce53475fbe2e214e4f68e4fc1d2fdf7a9 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:11:28 +0200 Subject: [PATCH 112/163] Move friends overview to new package and new stuff --- .../FriendsOverviewScreen.kt | 143 ++++++++++++++---- .../FriendsOverviewUiState.kt | 6 + .../FriendsOverviewViewModel.kt | 41 ++++- 3 files changed, 155 insertions(+), 35 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/{friend => friends/friends_overview}/FriendsOverviewScreen.kt (57%) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt rename app/src/main/java/be/ugent/sel/studeez/screens/{friend => friends/friends_overview}/FriendsOverviewViewModel.kt (53%) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt similarity index 57% rename from app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt index c9e80f5..7e2462d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewScreen.kt @@ -1,20 +1,21 @@ -package be.ugent.sel.studeez.screens.friend +package be.ugent.sel.studeez.screens.friends.friends_overview import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -22,7 +23,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicButton -import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.SearchField +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry import be.ugent.sel.studeez.common.ext.basicButton import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.data.local.models.User @@ -35,7 +37,11 @@ import be.ugent.sel.studeez.R.string as AppText data class FriendsOverviewActions( val getFriendsFlow: () -> Flow>>, - val searchFriends: () -> Unit + val searchFriends: () -> Unit, + val onQueryStringChange: (String) -> Unit, + val onSubmit: () -> Unit, + val viewProfile: (String) -> Unit, + val removeFriend: (Friendship) -> Unit ) fun getFriendsOverviewActions( @@ -44,7 +50,13 @@ fun getFriendsOverviewActions( ): FriendsOverviewActions { return FriendsOverviewActions( getFriendsFlow = viewModel::getAllFriends, - searchFriends = { viewModel.searchFriends(open) } + searchFriends = { viewModel.searchFriends(open) }, + onQueryStringChange = viewModel::onQueryStringChange, + onSubmit = { viewModel.onSubmit(open) }, + viewProfile = { userId -> + viewModel.viewProfile(userId, open) + }, + removeFriend = viewModel::removeFriend ) } @@ -54,27 +66,52 @@ fun FriendsOveriewRoute( popUp: () -> Unit, viewModel: FriendsOverviewViewModel ) { + val uiState by viewModel.uiState FriendsOverviewScreen( + popUp = popUp, + uiState = uiState, friendsOverviewActions = getFriendsOverviewActions( viewModel = viewModel, open = open - ), - popUp = popUp + ) ) } @Composable fun FriendsOverviewScreen( - friendsOverviewActions: FriendsOverviewActions, - popUp: () -> Unit + popUp: () -> Unit, + uiState: FriendsOverviewUiState, + friendsOverviewActions: FriendsOverviewActions ) { val friends = friendsOverviewActions.getFriendsFlow().collectAsState(initial = emptyList()) - - SecondaryScreenTemplate( - title = "TODO there needs to be a search field here", // TODO - popUp = popUp - ) { - LazyColumn { + + Scaffold( + topBar = { + TopAppBar( + title = { + // TODO Link to each other + SearchField( + value = uiState.queryString, + onValueChange = friendsOverviewActions.onQueryStringChange, + onSubmit = friendsOverviewActions.onSubmit, + label = AppText.search_friends + ) + }, + navigationIcon = { + IconButton(onClick = popUp) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = resources().getString(R.string.go_back) + ) + } + } + // TODO Add inbox action + ) + } + ) { paddingValues -> + LazyColumn ( + modifier = Modifier.padding(paddingValues) + ) { if (friends.value.isEmpty()) { // Show a quick button to search friends when the user does not have any friends yet. item { @@ -90,7 +127,9 @@ fun FriendsOverviewScreen( items(friends.value) { friend -> FriendsEntry( user = friend.first, - friendship = friend.second + friendship = friend.second, + viewProfile = { userId -> friendsOverviewActions.viewProfile(userId) }, + removeFriend = friendsOverviewActions.removeFriend ) } } @@ -102,11 +141,16 @@ fun FriendsOverviewScreen( fun FriendsOverviewPreview() { StudeezTheme { FriendsOverviewScreen( + popUp = {}, + uiState = FriendsOverviewUiState(""), friendsOverviewActions = FriendsOverviewActions( getFriendsFlow = { emptyFlow() }, - searchFriends = {} - ), - popUp = {} + searchFriends = {}, + onQueryStringChange = {}, + onSubmit = {}, + viewProfile = {}, + removeFriend = {} + ) ) } } @@ -114,7 +158,9 @@ fun FriendsOverviewPreview() { @Composable fun FriendsEntry( user: User, - friendship: Friendship + friendship: Friendship, + viewProfile: (String) -> Unit, + removeFriend: (Friendship) -> Unit ) { // TODO Styling Row ( @@ -162,7 +208,11 @@ fun FriendsEntry( Box( modifier = Modifier.fillMaxWidth(0.15f) ) { - ThreeDots(friendship = friendship) + FriendsOverviewDropDown( + friendship = friendship, + viewProfile = viewProfile, + removeFriend = removeFriend + ) } } } @@ -182,17 +232,23 @@ fun FriendsEntryPreview() { friendId = "someId", friendsSince = Timestamp.now(), accepted = true - ) + ), + viewProfile = {}, + removeFriend = {} ) } } @Composable -fun ThreeDots( - friendship: Friendship +fun FriendsOverviewDropDown( + friendship: Friendship, + viewProfile: (String) -> Unit, + removeFriend: (Friendship) -> Unit ) { + var expanded by remember { mutableStateOf(false) } + IconButton( - onClick = { /* TODO Open dropdown */ } + onClick = { expanded = true } ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), @@ -200,19 +256,40 @@ fun ThreeDots( modifier = Modifier.fillMaxSize() ) } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DrawerEntry( + icon = Icons.Default.Person, + text = stringResource(id = AppText.show_profile) + ) { + viewProfile(friendship.friendId) + } + DrawerEntry( + icon = Icons.Default.Delete, + text = stringResource(id = AppText.remove_friend) + ) { + removeFriend(friendship) + expanded = false + } + } } @Preview @Composable -fun ThreeDotsPreview() { +fun FriendsOverviewDropDownPreview() { StudeezTheme { - ThreeDots( + FriendsOverviewDropDown( friendship = Friendship( id = "", friendId = "someId", friendsSince = Timestamp.now(), accepted = true - ) + ), + viewProfile = {}, + removeFriend = { } ) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt new file mode 100644 index 0000000..8672814 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewUiState.kt @@ -0,0 +1,6 @@ +package be.ugent.sel.studeez.screens.friends.friends_overview + +data class FriendsOverviewUiState( + val userId: String, + val queryString: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt similarity index 53% rename from app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt index 0dec506..ce0c5af 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/friend/FriendsOverviewViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_overview/FriendsOverviewViewModel.kt @@ -1,5 +1,6 @@ -package be.ugent.sel.studeez.screens.friend +package be.ugent.sel.studeez.screens.friends.friends_overview +import androidx.compose.runtime.mutableStateOf import be.ugent.sel.studeez.data.local.models.Friendship import be.ugent.sel.studeez.data.local.models.User import be.ugent.sel.studeez.domain.FriendshipDAO @@ -7,6 +8,7 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel +import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -17,11 +19,19 @@ import javax.inject.Inject class FriendsOverviewViewModel @Inject constructor( private val userDAO: UserDAO, private val friendshipDAO: FriendshipDAO, + private val selectedProfileState: SelectedProfileState, logService: LogService ) : StudeezViewModel(logService) { + var uiState = mutableStateOf(FriendsOverviewUiState( + userId = selectedProfileState.selectedUserId + )) + private set + fun getAllFriends(): Flow>> { - return friendshipDAO.getAllFriendships() + return friendshipDAO.getAllFriendships( + userId = uiState.value.userId + ) .flatMapConcat { friendships -> val userFlows = friendships.map { friendship -> userDAO.getUserDetails(friendship.friendId) @@ -38,4 +48,31 @@ class FriendsOverviewViewModel @Inject constructor( open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) } + fun onQueryStringChange(newValue: String) { + uiState.value = uiState.value.copy( + queryString = newValue + ) + } + + fun onSubmit(open: (String) -> Unit) { + val query = uiState.value.queryString // TODO Pass as argument + open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) + } + + fun viewProfile( + userId: String, + open: (String) -> Unit + ) { + selectedProfileState.selectedUserId = userId + open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) + } + + fun removeFriend( + friendship: Friendship + ) { + friendshipDAO.removeFriendship( + friendship = friendship + ) + } + } \ No newline at end of file From a6a5fb5e958548d318054063dc3d37405cdd7337 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:11:55 +0200 Subject: [PATCH 113/163] Add public profile --- .../public_profile/PublicProfileScreen.kt | 178 ++++++++++++++++++ .../public_profile/PublicProfileUiState.kt | 5 + .../public_profile/PublicProfileViewModel.kt | 60 ++++++ .../public_profile/SelectedProfileState.kt | 12 ++ 4 files changed, 255 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt new file mode 100644 index 0000000..41e33c5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileScreen.kt @@ -0,0 +1,178 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MailOutline +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +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.Headline +import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.screens.profile.AmountOfFriendsButton +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import be.ugent.sel.studeez.R.string as AppText + +data class PublicProfileActions( + val getUserDetails: () -> Flow, + val getAmountOfFriends: () -> Flow, + val onViewFriendsClick: () -> Unit, + val sendFriendRequest: () -> Boolean +) + +fun getPublicProfileActions( + viewModel: PublicProfileViewModel, + open: (String) -> Unit +): PublicProfileActions { + return PublicProfileActions( + getUserDetails = { viewModel.getUserDetails(viewModel.uiState.value.userId) }, + getAmountOfFriends = { viewModel.getAmountOfFriends( + userId = viewModel.uiState.value.userId + ) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) }, + sendFriendRequest = { viewModel.sendFriendRequest( + userId = viewModel.uiState.value.userId + ) } + ) +} + +@Composable +fun PublicProfileRoute( + popUp: () -> Unit, + open: (String) -> Unit, + viewModel: PublicProfileViewModel +) { + PublicProfileScreen( + publicProfileActions = getPublicProfileActions( + viewModel = viewModel, + open = open + ), + popUp = popUp + ) +} + +@Composable +fun PublicProfileScreen( + publicProfileActions: PublicProfileActions, + popUp: () -> Unit +) { + val user = publicProfileActions.getUserDetails().collectAsState(initial = User()) + val amountOfFriends = publicProfileActions.getAmountOfFriends().collectAsState(initial = 0) + + SecondaryScreenTemplate( + title = stringResource(id = AppText.profile), + popUp = popUp, + barAction = { + PublicProfileEllipsis( + sendFriendRequest = publicProfileActions.sendFriendRequest + ) + } + ) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(15.dp) + ) { + item { + Headline(text = user.value.username) + } + + item { + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + modifier = Modifier + .fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally) + ) { + AmountOfFriendsButton( + amountOfFriends = amountOfFriends.value + ) { + publicProfileActions.onViewFriendsClick() + } + } + } + + item { + Text( + text = user.value.biography, + textAlign = TextAlign.Center, + modifier = Modifier.padding(48.dp, 0.dp) + ) + } + } + } +} + +@Preview +@Composable +fun PublicProfilePreview() { + StudeezTheme { + PublicProfileScreen( + publicProfileActions = PublicProfileActions( + getUserDetails = { + flowOf(User( + id = "someid", + username = "Maxime De Poorter", + biography = "I am a different student and this is my public profile" + )) + }, + getAmountOfFriends = { flowOf(113) }, + onViewFriendsClick = {}, + sendFriendRequest = { true } + ), + popUp = {} + ) + } +} + +@Composable +fun PublicProfileEllipsis( + sendFriendRequest: () -> Boolean +) { + var expanded by remember { mutableStateOf(false) } + + IconButton( + onClick = { expanded = true } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = resources().getString(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem(onClick = { expanded = false }) { + DrawerEntry( + icon = Icons.Default.MailOutline, + text = stringResource(id = AppText.send_friend_request) + ) { + sendFriendRequest() + } + } + } +} + +@Preview +@Composable +fun PublicProfileEllipsisPreview() { + StudeezTheme { + PublicProfileEllipsis( + sendFriendRequest = { true } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt new file mode 100644 index 0000000..537fed9 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileUiState.kt @@ -0,0 +1,5 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +data class PublicProfileUiState( + var userId: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt new file mode 100644 index 0000000..6cf22d9 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/PublicProfileViewModel.kt @@ -0,0 +1,60 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +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 kotlinx.coroutines.selects.select +import javax.inject.Inject + +@HiltViewModel +class PublicProfileViewModel @Inject constructor( + private val userDAO: UserDAO, + private val friendshipDAO: FriendshipDAO, + selectedProfileState: SelectedProfileState, + logService: LogService +): StudeezViewModel(logService) { + + val uiState = mutableStateOf( + PublicProfileUiState( + userId = selectedProfileState.selectedUserId + ) + ) + + fun getUserDetails( + userId: String + ): Flow { + uiState.value = uiState.value.copy( + userId = userId + ) + return userDAO.getUserDetails( + userId = uiState.value.userId + ) + } + + fun getAmountOfFriends( + userId: String + ): Flow { + return friendshipDAO.getFriendshipCount( + userId = userId + ) + } + + fun onViewFriendsClick( + open: (String) -> Unit + ) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } + + fun sendFriendRequest( + userId: String + ): Boolean { + return friendshipDAO.sendFriendshipRequest(userId) + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt new file mode 100644 index 0000000..4226d65 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/public_profile/SelectedProfileState.kt @@ -0,0 +1,12 @@ +package be.ugent.sel.studeez.screens.profile.public_profile + +import be.ugent.sel.studeez.domain.UserDAO +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SelectedProfileState @Inject constructor( + userDAO: UserDAO +) { + var selectedUserId: String = userDAO.getCurrentUserId() +} \ No newline at end of file From 566102d5d4868e2a4331666661e0ea6e74bba43f Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:13:13 +0200 Subject: [PATCH 114/163] More with friendship --- .../studeez/data/remote/FirebaseFriendship.kt | 7 ++ .../ugent/sel/studeez/domain/FriendshipDAO.kt | 18 ++-- .../implementation/FirebaseFriendshipDAO.kt | 83 ++++++++++++++++--- .../screens/profile/ProfileViewModel.kt | 3 +- 4 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt new file mode 100644 index 0000000..fb2af4b --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseFriendship.kt @@ -0,0 +1,7 @@ +package be.ugent.sel.studeez.data.remote + +object FirebaseFriendship { + const val FRIENDID: String = "friendId" + const val ACCEPTED: String = "accepted" + const val FRIENDSSINCE: String = "friendsSince" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt index eda933d..0beb01a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/FriendshipDAO.kt @@ -9,15 +9,19 @@ import kotlinx.coroutines.flow.Flow interface FriendshipDAO { /** - * @return all friendships of the user that is currently logged in. + * @return all friendships of a chosen user. */ - fun getAllFriendships(): Flow> + fun getAllFriendships( + userId: String + ): Flow> /** - * @return the amount of friends of the currently logged in user. + * @return the amount of friends of a chosen user. * This method should be faster than just counting the length of getAllFriends() */ - fun getFriendshipCount(): Flow + fun getFriendshipCount( + userId: String + ): Flow /** * @param id the id of the friendship that you want details of @@ -41,8 +45,10 @@ interface FriendshipDAO { /** * Remove a friend or decline a friendrequest. - * @param id of the friendship that you want to update + * @param friendship the one you want to remove * @return: Success/faillure of transaction */ - fun removeFriendship(id: String): Boolean + fun removeFriendship( + friendship: Friendship + ): Boolean } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt index 4665e0f..bd429e1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseFriendshipDAO.kt @@ -1,9 +1,16 @@ package be.ugent.sel.studeez.domain.implementation +import androidx.compose.runtime.collectAsState import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.Friendship +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.ACCEPTED +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.FRIENDSSINCE +import be.ugent.sel.studeez.data.remote.FirebaseFriendship.FRIENDID import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.FriendshipDAO +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.FRIENDS_COLLECTION +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.USER_COLLECTION +import com.google.firebase.Timestamp import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots @@ -23,21 +30,29 @@ class FirebaseFriendshipDAO @Inject constructor( ): FriendshipDAO { private fun currentUserDocument(): DocumentReference = firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .document(auth.currentUserId) - override fun getAllFriendships(): Flow> { - return currentUserDocument() - .collection(FirebaseCollections.FRIENDS_COLLECTION) + override fun getAllFriendships( + userId: String + ): Flow> { + return firestore + .collection(USER_COLLECTION) + .document(userId) + .collection(FRIENDS_COLLECTION) .snapshots() .map { it.toObjects(Friendship::class.java) } } - override fun getFriendshipCount(): Flow { + override fun getFriendshipCount( + userId: String + ): Flow { return flow { val friendshipCount = suspendCoroutine { continuation -> - currentUserDocument() - .collection(FirebaseCollections.FRIENDS_COLLECTION) + firestore + .collection(USER_COLLECTION) + .document(userId) + .collection(FRIENDS_COLLECTION) .get() .addOnSuccessListener { querySnapshot -> continuation.resume(querySnapshot.size()) @@ -57,15 +72,63 @@ class FirebaseFriendshipDAO @Inject constructor( } override fun sendFriendshipRequest(id: String): Boolean { - TODO("Not yet implemented") + val currentUserId: String = auth.currentUserId + val otherUserId: String = id + + // Add entry to current user + currentUserDocument() + .collection(FRIENDS_COLLECTION) + .add(mapOf( + FRIENDID to otherUserId, + ACCEPTED to true, // TODO Make it not automatically accepted. + FRIENDSSINCE to Timestamp.now() + )) + + // Add entry to other user + firestore.collection(USER_COLLECTION) + .document(otherUserId) + .collection(FRIENDS_COLLECTION) + .add(mapOf( + FRIENDID to currentUserId, + ACCEPTED to true, // TODO Make it not automatically accepted. + FRIENDSSINCE to Timestamp.now() + )) + + return true } override fun acceptFriendship(id: String): Boolean { TODO("Not yet implemented") } - override fun removeFriendship(id: String): Boolean { - TODO("Not yet implemented") + override fun removeFriendship( + friendship: Friendship + ): Boolean { + val currentUserId: String = auth.currentUserId + val otherUserId: String = friendship.friendId + + // Remove at logged in user + firestore.collection(USER_COLLECTION) + .document(currentUserId) + .collection(FRIENDS_COLLECTION) + .document(friendship.id) + .delete() + + // Remove at other user + firestore.collection(USER_COLLECTION) + .document(otherUserId) + .collection(FRIENDS_COLLECTION) + .whereEqualTo(FRIENDID, currentUserId) + .get() + .addOnSuccessListener { + for (document in it) { + document.reference.delete() + } + }.addOnFailureListener { + SnackbarManager.showMessage(AppText.generic_error) + } + + return true } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt index e485de6..93fa086 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt @@ -5,6 +5,7 @@ import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.UserDAO import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.screens.StudeezViewModel +import com.google.firebase.auth.FirebaseAuth import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -25,7 +26,7 @@ class ProfileViewModel @Inject constructor( } fun getAmountOfFriends(): Flow { - return friendshipDAO.getFriendshipCount() + return friendshipDAO.getFriendshipCount(userDAO.getCurrentUserId()) } fun onEditProfileClick(open: (String) -> Unit) { From 7c95e78b2a8eb168ce25cbf9049ad1a243a201e9 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:14:26 +0200 Subject: [PATCH 115/163] More with UserDAO --- .../sel/studeez/data/remote/FirebaseUser.kt | 6 ++++ .../be/ugent/sel/studeez/domain/UserDAO.kt | 8 +++-- .../domain/implementation/FirebaseUserDAO.kt | 34 ++++++++++++------- 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt new file mode 100644 index 0000000..9ee5aa2 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/remote/FirebaseUser.kt @@ -0,0 +1,6 @@ +package be.ugent.sel.studeez.data.remote + +object FirebaseUser { + const val USERNAME: String = "username" + const val BIOGRAPHY: String = "biography" +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt index 6017342..09179f1 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt @@ -5,6 +5,8 @@ import kotlinx.coroutines.flow.Flow interface UserDAO { + fun getCurrentUserId(): String + /** * @return all users */ @@ -13,8 +15,10 @@ interface UserDAO { /** * @return all users based on a query, a trimmed down version of getAllUsers() */ - fun getUsersWithQuery(): Flow> - // TODO Add query parameter + fun getUsersWithQuery( + fieldName: String, + value: String + ): Flow> /** * Request information about a user diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt index 8c84914..f41fcd9 100644 --- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt @@ -3,8 +3,11 @@ package be.ugent.sel.studeez.domain.implementation import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.snackbar.SnackbarManager import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.data.remote.FirebaseUser.BIOGRAPHY +import be.ugent.sel.studeez.data.remote.FirebaseUser.USERNAME import be.ugent.sel.studeez.domain.AccountDAO import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.domain.implementation.FirebaseCollections.USER_COLLECTION import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.snapshots @@ -17,12 +20,10 @@ import javax.inject.Inject class FirebaseUserDAO @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountDAO - ) : UserDAO { +) : UserDAO { - companion object { - private const val USER_COLLECTION = FirebaseCollections.USER_COLLECTION - private const val USERNAME_FIELD = "username" - private const val BIOGRAPHY_FIELD = "biography" + override fun getCurrentUserId(): String { + return auth.currentUserId } private fun currentUserDocument(): DocumentReference = @@ -32,19 +33,26 @@ class FirebaseUserDAO @Inject constructor( override fun getAllUsers(): Flow> { return firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .snapshots() .map { it.toObjects(User::class.java) } } - override fun getUsersWithQuery(): Flow> { - TODO("Not yet implemented") + override fun getUsersWithQuery( + fieldName: String, + value: String + ): Flow> { + return firestore + .collection(USER_COLLECTION) + .whereEqualTo(fieldName, value) + .snapshots() + .map { it.toObjects(User::class.java) } } override fun getUserDetails(userId: String): Flow { return flow { val snapshot = firestore - .collection(FirebaseCollections.USER_COLLECTION) + .collection(USER_COLLECTION) .document(userId) .get() .await() @@ -56,8 +64,8 @@ class FirebaseUserDAO @Inject constructor( override suspend fun getLoggedInUser(): User { val userDocument = currentUserDocument().get().await() return User( - username = userDocument.getString(USERNAME_FIELD) ?: "", - biography = userDocument.getString(BIOGRAPHY_FIELD) ?: "" + username = userDocument.getString(USERNAME) ?: "", + biography = userDocument.getString(BIOGRAPHY) ?: "" ) } @@ -66,8 +74,8 @@ class FirebaseUserDAO @Inject constructor( newBiography: String ) { currentUserDocument().set(mapOf( - USERNAME_FIELD to newUsername, - BIOGRAPHY_FIELD to newBiography + USERNAME to newUsername, + BIOGRAPHY to newBiography )) } From 74a6f77261661728761380ff21c54fae25fed211 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:14:49 +0200 Subject: [PATCH 116/163] Add friends search screen --- .../friends_search/SearchFriendUiState.kt | 10 + .../friends_search/SearchFriendsScreen.kt | 269 ++++++++++++++++++ .../friends_search/SearchFriendsViewModel.kt | 57 ++++ 3 files changed, 336 insertions(+) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt new file mode 100644 index 0000000..0a5a10f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendUiState.kt @@ -0,0 +1,10 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import be.ugent.sel.studeez.data.local.models.User +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +data class SearchFriendUiState( + val queryString: String = "", + val searchResults: Flow> = emptyFlow() +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt new file mode 100644 index 0000000..a91d0f4 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsScreen.kt @@ -0,0 +1,269 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import be.ugent.sel.studeez.R +import be.ugent.sel.studeez.common.composable.SearchField +import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.resources +import be.ugent.sel.studeez.ui.theme.StudeezTheme +import kotlinx.coroutines.flow.* +import be.ugent.sel.studeez.R.string as AppText + +data class SearchFriendsActions( + val onQueryStringChange: (String) -> Unit, + val getUsersWithUsername: (String) -> Unit, + val getAllUsers: () -> Flow>, + val goToProfile: (String) -> Unit +) + +fun getSearchFriendsActions( + viewModel: SearchFriendsViewModel, + open: (String) -> Unit +): SearchFriendsActions { + return SearchFriendsActions( + onQueryStringChange = viewModel::onQueryStringChange, + getUsersWithUsername = viewModel::getUsersWithUsername, + getAllUsers = { viewModel.getAllUsers() }, + goToProfile = { userId -> viewModel.goToProfile(userId, open) } + ) +} + +@Composable +fun SearchFriendsRoute( + popUp: () -> Unit, + open: (String) -> Unit, + viewModel: SearchFriendsViewModel +) { + val uiState by viewModel.uiState + + SearchFriendsScreen( + popUp = popUp, + uiState = uiState, + searchFriendsActions = getSearchFriendsActions( + viewModel = viewModel, + open = open + ) + ) +} + +@Composable +fun SearchFriendsScreen( + popUp: () -> Unit, + uiState: SearchFriendUiState, + searchFriendsActions: SearchFriendsActions +) { + var query by remember { mutableStateOf(uiState.queryString) } + val searchResults = searchFriendsActions.getAllUsers().collectAsState( + initial = emptyList() + ) + + Scaffold( + topBar = { + TopAppBar( + title = { + SearchField( + value = query, + onValueChange = { newValue -> + searchFriendsActions.onQueryStringChange(newValue) + query = newValue + }, + onSubmit = { }, + label = AppText.search_friends + ) + }, + navigationIcon = { + IconButton(onClick = popUp) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = resources().getString(R.string.go_back) + ) + } + } + ) + } + ) { paddingValues -> + LazyColumn( + modifier = Modifier.padding(paddingValues) + ) { + items (searchResults.value) { user -> + UserEntry( + user = user, + goToProfile = searchFriendsActions.goToProfile + ) + } + } + } +} + +@Preview +@Composable +fun SearchFriendsPreview() { + StudeezTheme { + SearchFriendsScreen( + popUp = {}, + uiState = SearchFriendUiState( + queryString = "dit is een test", + searchResults = flowOf(listOf(User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ))) + ), + searchFriendsActions = SearchFriendsActions( + onQueryStringChange = {}, + getUsersWithUsername = {}, + getAllUsers = { + flowOf(listOf(User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ))) + }, + goToProfile = { } + ) + ) + } +} + +@Composable +fun UserEntry( + user: User, + goToProfile: (String) -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.15f) + .background(MaterialTheme.colors.primary, CircleShape) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_visibility_on), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .align(Alignment.Center), + tint = MaterialTheme.colors.onPrimary + ) + } + + Box ( + modifier = Modifier + .fillMaxWidth(0.65f) + ) { + Column ( + modifier = Modifier + .padding(vertical = 4.dp) + ) { + Text( + text = user.username, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = "${resources().getString(AppText.app_name)} ${resources().getString(AppText.friend)}", + fontSize = 14.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Box( + modifier = Modifier.fillMaxWidth(0.15f) + ) { + SearchFriendsDropDown( + user = user, + goToProfile = goToProfile + ) + } + } +} + +@Preview +@Composable +fun UserEntryPreview() { + StudeezTheme { + UserEntry( + user = User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ), + goToProfile = { } + ) + } +} + +/** + * Three dots that open a dropdown menu that allow to go the users profile. + */ +@Composable +fun SearchFriendsDropDown( + user: User, + goToProfile: (String) -> Unit +) { + var expanded by remember { mutableStateOf(false) } + + IconButton( + onClick = { expanded = true } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), + contentDescription = stringResource(AppText.view_more), + modifier = Modifier.fillMaxSize() + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem(onClick = { expanded = false }) { + DrawerEntry( + icon = Icons.Default.Person, + text = stringResource(id = AppText.show_profile) + ) { + goToProfile(user.id) + } + } + } +} + +@Preview +@Composable +fun SearchFriendsDropDownPreview() { + StudeezTheme { + SearchFriendsDropDown( + user = User( + id = "someid", + username = "Eerste user", + biography = "blah blah blah" + ), + goToProfile = { } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt new file mode 100644 index 0000000..a25e1a7 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/friends/friends_search/SearchFriendsViewModel.kt @@ -0,0 +1,57 @@ +package be.ugent.sel.studeez.screens.friends.friends_search + +import androidx.compose.runtime.mutableStateOf +import be.ugent.sel.studeez.data.local.models.User +import be.ugent.sel.studeez.data.remote.FirebaseUser +import be.ugent.sel.studeez.domain.LogService +import be.ugent.sel.studeez.domain.UserDAO +import be.ugent.sel.studeez.navigation.StudeezDestinations +import be.ugent.sel.studeez.screens.StudeezViewModel +import be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +@HiltViewModel +class SearchFriendsViewModel @Inject constructor( + private val userDAO: UserDAO, + private val selectedProfileState: SelectedProfileState, + logService: LogService +): StudeezViewModel(logService) { + + var uiState = mutableStateOf(SearchFriendUiState()) + private set + + fun onQueryStringChange(newValue: String) { + uiState.value = uiState.value.copy( + queryString = newValue + ) + uiState.value = uiState.value.copy( + searchResults = userDAO.getUsersWithQuery( + fieldName = FirebaseUser.USERNAME, + value = uiState.value.queryString + ) + ) + } + + fun getUsersWithUsername( + value: String + ): Flow> { + return userDAO.getUsersWithQuery( + fieldName = FirebaseUser.USERNAME, + value = value + ) + } + + fun getAllUsers(): Flow> { + return userDAO.getAllUsers() + } + + fun goToProfile( + userId: String, + open: (String) -> Unit + ) { + selectedProfileState.selectedUserId = userId + open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) + } +} \ No newline at end of file From f03a06b24594a32b13811018128a1abfc11d401a Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:15:10 +0200 Subject: [PATCH 117/163] Update navigation --- .../studeez/navigation/StudeezDestinations.kt | 1 + .../sel/studeez/navigation/StudeezNavGraph.kt | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index 2df3899..578c74a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -32,6 +32,7 @@ object StudeezDestinations { // Friends flow const val FRIENDS_OVERVIEW_SCREEN = "friends_overview" const val SEARCH_FRIENDS_SCREEN = "search_friends" + const val PUBLIC_PROFILE_SCREEN = "public_profile" // Create & edit screens const val CREATE_TASK_SCREEN = "create_task" diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt index 0c4b803..d6264fc 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezNavGraph.kt @@ -14,10 +14,13 @@ import be.ugent.sel.studeez.common.composable.drawer.getDrawerActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions +import be.ugent.sel.studeez.screens.friends.friends_overview.FriendsOveriewRoute +import be.ugent.sel.studeez.screens.friends.friends_search.SearchFriendsRoute import be.ugent.sel.studeez.screens.home.HomeRoute import be.ugent.sel.studeez.screens.log_in.LoginRoute -import be.ugent.sel.studeez.screens.profile.EditProfileRoute +import be.ugent.sel.studeez.screens.profile.edit_profile.EditProfileRoute import be.ugent.sel.studeez.screens.profile.ProfileRoute +import be.ugent.sel.studeez.screens.profile.public_profile.PublicProfileRoute import be.ugent.sel.studeez.screens.session.SessionRoute import be.ugent.sel.studeez.screens.session_recap.SessionRecapRoute import be.ugent.sel.studeez.screens.sessions.SessionsRoute @@ -221,11 +224,27 @@ fun StudeezNavGraph( // Friends flow composable(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) { - // TODO + FriendsOveriewRoute( + open = open, + popUp = goBack, + viewModel = hiltViewModel() + ) } composable(StudeezDestinations.SEARCH_FRIENDS_SCREEN) { - // TODO + SearchFriendsRoute( + popUp = goBack, + open = open, + viewModel = hiltViewModel() + ) + } + + composable(StudeezDestinations.PUBLIC_PROFILE_SCREEN) { + PublicProfileRoute( + popUp = goBack, + open = open, + viewModel = hiltViewModel() + ) } // Create & edit screens From b7ffc7d802a4f3b08c0f415793d658982ac6c2db Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:15:36 +0200 Subject: [PATCH 118/163] Add searchfield composable --- .../common/composable/TextFieldComposable.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt index aadcee3..dfd989c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt @@ -1,6 +1,7 @@ package be.ugent.sel.studeez.common.composable import androidx.annotation.StringRes +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -10,6 +11,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Email import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Search import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -22,6 +24,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import be.ugent.sel.studeez.common.ext.fieldModifier import be.ugent.sel.studeez.resources +import com.google.android.material.color.MaterialColors import kotlin.math.sin import be.ugent.sel.studeez.R.drawable as AppIcon import be.ugent.sel.studeez.R.string as AppText @@ -218,4 +221,34 @@ private fun PasswordField( keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), visualTransformation = visualTransformation ) +} + +@Composable +fun SearchField( + value: String, + onValueChange: (String) -> Unit, + onSubmit: () -> Unit, + @StringRes label: Int, + modifier: Modifier = Modifier +) { + OutlinedTextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + label = { Text(text = stringResource(id = label)) }, + trailingIcon = { + IconButton(onClick = onSubmit) { + Icon( + imageVector = Icons.Default.Search, + contentDescription = stringResource(label), + tint = MaterialTheme.colors.primary + ) + } + }, + singleLine = true, + colors = TextFieldDefaults.outlinedTextFieldColors( + textColor = MaterialTheme.colors.onBackground, + backgroundColor = MaterialTheme.colors.background + ) + ) } \ No newline at end of file From 70f1be9b2223641a37a12504303237870b3bba01 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:16:11 +0200 Subject: [PATCH 119/163] Add friends button to home screen --- .../be/ugent/sel/studeez/screens/home/HomeScreen.kt | 11 ++++++++--- .../ugent/sel/studeez/screens/home/HomeViewModel.kt | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index f02852e..689e512 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -24,6 +24,7 @@ fun HomeRoute( ) { HomeScreen( onStartSessionClick = { viewModel.onStartSessionClick(open) }, + onViewFriendsClick = { viewModel.onViewFriendsClick(open) }, drawerActions = drawerActions, navigationBarActions = navigationBarActions, ) @@ -32,6 +33,7 @@ fun HomeRoute( @Composable fun HomeScreen( onStartSessionClick: () -> Unit, + onViewFriendsClick: () -> Unit, drawerActions: DrawerActions, navigationBarActions: NavigationBarActions ) { @@ -39,7 +41,7 @@ fun HomeScreen( title = resources().getString(R.string.home), drawerActions = drawerActions, navigationBarActions = navigationBarActions, - // TODO barAction = { FriendsAction() } + barAction = { FriendsAction(onViewFriendsClick) } ) { BasicButton(R.string.start_session, Modifier.basicButton()) { onStartSessionClick() @@ -48,8 +50,10 @@ fun HomeScreen( } @Composable -fun FriendsAction() { - IconButton(onClick = { /*TODO*/ }) { +fun FriendsAction( + onClick: () -> Unit +) { + IconButton(onClick = onClick) { Icon( imageVector = Icons.Default.Person, contentDescription = resources().getString(R.string.friends) @@ -62,6 +66,7 @@ fun FriendsAction() { fun HomeScreenPreview() { HomeScreen( onStartSessionClick = {}, + onViewFriendsClick = {}, drawerActions = DrawerActions({}, {}, {}, {}, {}), navigationBarActions = NavigationBarActions({ false }, {}, {}, {}, {}, {}, {}, {}) ) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt index b27f995..e757b15 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt @@ -16,4 +16,8 @@ class HomeViewModel @Inject constructor( fun onStartSessionClick(open: (String) -> Unit) { open(StudeezDestinations.TIMER_SELECTION_SCREEN) } + + fun onViewFriendsClick(open: (String) -> Unit) { + open(StudeezDestinations.FRIENDS_OVERVIEW_SCREEN) + } } \ No newline at end of file From 10c86c9bf088216866ba3d7e2e00130342ed96f6 Mon Sep 17 00:00:00 2001 From: tdpeuter Date: Mon, 15 May 2023 14:16:30 +0200 Subject: [PATCH 120/163] Add resources --- app/src/main/res/values/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ae1bc8..4a72fdc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,6 +114,10 @@ Adding friends still needs to be implemented. Hang on tight! You don\'t have any friends yet. Add one! + Search friends + Send friend request + Remove as friend + Show profile From 522ecae87a1ebcd21b5583affa43004674c23bce Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Mon, 15 May 2023 15:11:50 +0200 Subject: [PATCH 121/163] fist test --- .idea/androidTestResultsUserPreferences.xml | 50 +++++++++++++ .idea/misc.xml | 3 +- app/build.gradle | 10 ++- .../be/ugent/sel/studeez/SessionScreenTest.kt | 70 +++++++++++++++++++ .../InvisibleSessionManagerTest.kt | 1 - build.gradle | 7 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 8 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 .idea/androidTestResultsUserPreferences.xml create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml new file mode 100644 index 0000000..a751b96 --- /dev/null +++ b/.idea/androidTestResultsUserPreferences.xml @@ -0,0 +1,50 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..773fe0f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,6 @@ - - + diff --git a/app/build.gradle b/app/build.gradle index a19cbd7..bc5cfaf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,8 @@ dependencies { implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.compose.material:material:1.2.0' + implementation 'androidx.test.ext:junit-ktx:1.1.5' + implementation 'androidx.navigation:navigation-testing:2.5.3' debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" @@ -96,13 +98,17 @@ dependencies { // JUnit testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" // Coroutine testing testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' // Mocking testImplementation 'org.mockito.kotlin:mockito-kotlin:3.2.0' + androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1' + androidTestImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0" // Networking implementation 'com.squareup.retrofit2:retrofit:2.9.0' @@ -111,8 +117,6 @@ dependencies { testImplementation 'androidx.arch.core:core-testing:2.1.0' // GUI testing - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" //Firebase diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt new file mode 100644 index 0000000..65fb5eb --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt @@ -0,0 +1,70 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.data.local.models.SessionReport +import be.ugent.sel.studeez.screens.session_recap.SessionRecapActions +import be.ugent.sel.studeez.screens.session_recap.SessionRecapScreen +import com.google.firebase.Timestamp +import org.junit.Assert +import org.junit.Rule +import org.junit.Test + + +class SessionScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun SessionRecapTest() { + var saveCalled = false + var discardCalled = false + + composeTestRule.setContent { + SessionRecapScreen( + Modifier, + SessionRecapActions( + { + SessionReport( + "", + 0, + Timestamp(0, 0), + "") + }, + { saveCalled = true }, + { discardCalled = true } + ) + ) + } + composeTestRule + .onNodeWithText( + "You studied", + substring = true, + ignoreCase = true + ) + .assertExists() + + composeTestRule + .onNodeWithText( + "save", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + composeTestRule + .onNodeWithText( + "discard", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + + Assert.assertTrue(saveCalled) + Assert.assertTrue(discardCalled) + } +} diff --git a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt index 54f673d..0c973a5 100644 --- a/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt +++ b/app/src/test/java/be/ugent/sel/studeez/timer_functional/InvisibleSessionManagerTest.kt @@ -6,7 +6,6 @@ import be.ugent.sel.studeez.data.SelectedTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer -import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.implementation.LogServiceImpl import be.ugent.sel.studeez.screens.session.InvisibleSessionManager import be.ugent.sel.studeez.screens.session.SessionViewModel diff --git a/build.gradle b/build.gradle index 7f25617..7f70dd1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,5 @@ buildscript { ext { - compose_ui_version = '1.2.0' compose_version = '1.1.1' coreTestingVersion = '2.1.0' espressoVersion = '3.4.0' @@ -9,13 +8,13 @@ buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:8.0.0' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.4.2' apply false - id 'com.android.library' version '7.4.2' apply false + id 'com.android.application' version '8.0.0' apply false + id 'com.android.library' version '8.0.0' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false // Hilt diff --git a/gradle.properties b/gradle.properties index edf11ef..8581bd2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,6 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 435a2d6..a38b6fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sat Mar 25 16:03:50 CET 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 74526bc98451a3c30177b4e92a60ba35b82f5f92 Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Mon, 15 May 2023 15:58:59 +0200 Subject: [PATCH 122/163] proto error --- .idea/androidTestResultsUserPreferences.xml | 26 ++++++++++++ app/build.gradle | 12 ++---- .../be/ugent/sel/studeez/HomeScreenTest.kt | 42 +++++++++++++++++++ ...nstrumentedTest.kt => InstrumentedTest.kt} | 2 +- .../be/ugent/sel/studeez/SessionScreenTest.kt | 4 +- build.gradle | 8 ++-- 6 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt rename app/src/androidTest/java/be/ugent/sel/studeez/{ExampleInstrumentedTest.kt => InstrumentedTest.kt} (95%) diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index a751b96..59f6bc1 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -16,6 +16,19 @@ + + + + + + + @@ -44,6 +57,19 @@ + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index bc5cfaf..197b055 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,8 +65,6 @@ dependencies { implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.compose.material:material:1.2.0' - implementation 'androidx.test.ext:junit-ktx:1.1.5' - implementation 'androidx.navigation:navigation-testing:2.5.3' debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" @@ -98,17 +96,13 @@ dependencies { // JUnit testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" + androidTestImplementation 'androidx.test.ext:junit:1.1.5' // Coroutine testing testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' // Mocking testImplementation 'org.mockito.kotlin:mockito-kotlin:3.2.0' - androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1' - androidTestImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0" // Networking implementation 'com.squareup.retrofit2:retrofit:2.9.0' @@ -117,6 +111,8 @@ dependencies { testImplementation 'androidx.arch.core:core-testing:2.1.0' // GUI testing + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" //Firebase @@ -154,4 +150,4 @@ protobuf { } } } -} +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt new file mode 100644 index 0000000..fa60dee --- /dev/null +++ b/app/src/androidTest/java/be/ugent/sel/studeez/HomeScreenTest.kt @@ -0,0 +1,42 @@ +package be.ugent.sel.studeez + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import be.ugent.sel.studeez.common.composable.drawer.DrawerActions +import be.ugent.sel.studeez.common.composable.feed.FeedUiState +import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions +import be.ugent.sel.studeez.data.local.models.FeedEntry +import be.ugent.sel.studeez.screens.home.HomeScreen +import org.junit.Rule +import org.junit.Test + +class HomeScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun sessionRecapTestt() { + var saveCalled = false + + composeTestRule.setContent { + HomeScreen( + open = {}, + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({false}, {}, {}, {}, {}, {}, {}, {}), + feedUiState = FeedUiState.Succes(mapOf()), + continueTask = {_, _ -> }, + onEmptyFeedHelp = {} + ) + } + + composeTestRule + .onNodeWithText( + "continue", + substring = true, + ignoreCase = true + ) + .assertExists() + .performClick() + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/ExampleInstrumentedTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/InstrumentedTest.kt similarity index 95% rename from app/src/androidTest/java/be/ugent/sel/studeez/ExampleInstrumentedTest.kt rename to app/src/androidTest/java/be/ugent/sel/studeez/InstrumentedTest.kt index 06f9435..d6a1522 100644 --- a/app/src/androidTest/java/be/ugent/sel/studeez/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/be/ugent/sel/studeez/InstrumentedTest.kt @@ -14,7 +14,7 @@ import org.junit.Assert.* * See [testing documentation](http://d.android.com/tools/testing). */ @RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { +class InstrumentedTest { @Test fun useAppContext() { // Context of the app under test. diff --git a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt index 65fb5eb..995f0a2 100644 --- a/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt +++ b/app/src/androidTest/java/be/ugent/sel/studeez/SessionScreenTest.kt @@ -18,7 +18,7 @@ class SessionScreenTest { val composeTestRule = createComposeRule() @Test - fun SessionRecapTest() { + fun sessionRecapTest() { var saveCalled = false var discardCalled = false @@ -67,4 +67,6 @@ class SessionScreenTest { Assert.assertTrue(saveCalled) Assert.assertTrue(discardCalled) } + + } diff --git a/build.gradle b/build.gradle index 7f70dd1..4535dd7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ buildscript { ext { + compose_ui_version = '1.2.0' compose_version = '1.1.1' coreTestingVersion = '2.1.0' espressoVersion = '3.4.0' @@ -8,16 +9,15 @@ buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' - classpath 'com.android.tools.build:gradle:8.0.0' + classpath 'com.android.tools.build:gradle:7.2.0' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '8.0.0' apply false - id 'com.android.library' version '8.0.0' apply false + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false // Hilt id 'com.google.dagger.hilt.android' version '2.44' apply false } - From b614f7d530204521f910892466893f13fe87769c Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 16:30:13 +0200 Subject: [PATCH 123/163] Dots now returns an int --- .../composables/BreakTimerScreenComposable.kt | 21 +++++++++++-------- .../composables/GetSessionScreenComposable.kt | 4 ++++ .../composables/SessionScreenComposable.kt | 4 ++-- .../composables/TimerComposable.kt | 4 ++-- 4 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index b548591..89bcc24 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -3,6 +3,7 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -18,18 +19,18 @@ import be.ugent.sel.studeez.screens.session.SessionActions fun BreakSessionScreenComposable( open: (String) -> Unit, sessionActions: SessionActions, - pomodoroTimer: FunctionalPomodoroTimer + pomodoroTimer: FunctionalPomodoroTimer, ) { SessionScreen( open = open, sessionActions = sessionActions, - midSection = { Dots(pomodoroTimer) }, - motivationString = { motivationString(pomodoroTimer = pomodoroTimer) } + midSection = { Dots(pomodoroTimer = pomodoroTimer) }, + motivationString = { motivationString (pomodoroTimer = pomodoroTimer) } ) } @Composable -private fun Dots(pomodoroTimer: FunctionalPomodoroTimer) { +private fun Dots(pomodoroTimer: FunctionalPomodoroTimer): Int { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, @@ -43,6 +44,7 @@ private fun Dots(pomodoroTimer: FunctionalPomodoroTimer) { Dot(color = Color.Gray) } } + return pomodoroTimer.breaksRemaining } @Composable @@ -65,9 +67,10 @@ private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { return resources().getString(R.string.state_done) } - return resources().getQuantityString( - R.plurals.state_focus_remaining, - pomodoroTimer.breaksRemaining, - pomodoroTimer.breaksRemaining - ) + return resources().getString(R.string.state_focus) +} + +@Composable +private fun test(pomodoroTimer: FunctionalPomodoroTimer): String { + return pomodoroTimer.breaksRemaining.toString() } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt new file mode 100644 index 0000000..3f8762b --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens.composables + +class GetSessionScreenComposable { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt index 5ed29f8..b31ee45 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -23,7 +23,7 @@ import be.ugent.sel.studeez.screens.session.SessionActions fun SessionScreen( open: (String) -> Unit, sessionActions: SessionActions, - midSection: @Composable () -> Unit = {}, + midSection: @Composable () -> Int = {0}, motivationString: @Composable () -> String, ) { @@ -33,7 +33,7 @@ fun SessionScreen( Timer( sessionActions = sessionActions, motivationString = motivationString, - midSection = midSection + MidSection = midSection ) Box( contentAlignment = Alignment.Center, modifier = Modifier diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index 3b65599..87a6839 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -24,7 +24,7 @@ import kotlin.time.Duration.Companion.seconds fun Timer( sessionActions: SessionActions, motivationString: @Composable () -> String, - midSection: @Composable () -> Unit + MidSection: @Composable () -> Int ) { var tikker by remember { mutableStateOf(false) } LaunchedEffect(tikker) { @@ -39,7 +39,7 @@ fun Timer( TimerClock(hms) MotivationText(text = motivationString()) - + MidSection() Box( contentAlignment = Alignment.Center, modifier = Modifier From 052ebb8c32996f48ec376d17cab9e35891b3802d Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 16:31:36 +0200 Subject: [PATCH 124/163] #118 new visitor for session screen --- .../composables/GetSessionScreenComposable.kt | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt index 3f8762b..bae4d92 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt @@ -1,4 +1,49 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables -class GetSessionScreenComposable { +import android.media.MediaPlayer +import androidx.compose.runtime.Composable +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimerVisitor +import be.ugent.sel.studeez.screens.session.SessionActions +import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen +import be.ugent.sel.studeez.screens.session.sessionScreens.BreakSessionScreen +import be.ugent.sel.studeez.screens.session.sessionScreens.CustomSessionScreen +import be.ugent.sel.studeez.screens.session.sessionScreens.EndlessSessionScreen + +class GetSessionScreenComposable( + private val mediaplayer: MediaPlayer?, + private val open: (String) -> Unit, + private val sessionActions: SessionActions + ) : + FunctionalTimerVisitor<@Composable () -> Unit> { + + override fun visitFunctionalCustomTimer(functionalCustomTimer: FunctionalCustomTimer): @Composable () -> Unit { + return { CustomTimerSessionScreenComposable( + open = open, + sessionActions = sessionActions, + customTimer = functionalCustomTimer + ) + } + } + + override fun visitFunctionalEndlessTimer(functionalEndlessTimer: FunctionalEndlessTimer): @Composable () -> Unit { + return { + EndlessTimerSessionScreenComposable( + open = open, + sessionActions = sessionActions, + ) + } + } + + override fun visitFunctionalBreakTimer(functionalPomodoroTimer: FunctionalPomodoroTimer): @Composable () -> Unit { + return { + BreakSessionScreenComposable( + open = open, + sessionActions = sessionActions, + pomodoroTimer = functionalPomodoroTimer + ) + } + } } \ No newline at end of file From 4466f3646f9d730be0b1f8009be25174552a2cfc Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 16:34:19 +0200 Subject: [PATCH 125/163] #118 gebruik nieuwe visitor om sessionScreen te instantieren --- .../ugent/sel/studeez/screens/session/SessionRoute.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt index 084ff43..959bc74 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt @@ -5,9 +5,12 @@ import android.media.RingtoneManager import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext +import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreen +import be.ugent.sel.studeez.screens.session.sessionScreens.composables.BreakSessionScreenComposable +import be.ugent.sel.studeez.screens.session.sessionScreens.composables.GetSessionScreenComposable data class SessionActions( val getTimer: () -> FunctionalTimer, @@ -47,10 +50,8 @@ fun SessionRoute( mediaplayer = mediaplayer ) - val sessionScreen: AbstractSessionScreen = viewModel.getTimer().accept(GetSessionScreen(mediaplayer)) + val sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer) + val sessionScreen = viewModel.getTimer().accept(GetSessionScreenComposable(mediaplayer, open, sessionActions)) - sessionScreen( - open = open, - sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer) - ) + sessionScreen() } From 4519bf2e3037766c979a8f32b485dbe7f8bfee5a Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 18:57:24 +0200 Subject: [PATCH 126/163] added soundplayer --- .../session/sessionScreens/GetSessionScreen.kt | 18 ------------------ .../session/sessionScreens/SoundPlayer.kt | 4 ++++ .../composables/BreakTimerScreenComposable.kt | 9 +++------ .../CustomTimerSessionScreenComposable.kt | 5 ++++- 4 files changed, 11 insertions(+), 25 deletions(-) delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt deleted file mode 100644 index 98b2d5e..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreen.kt +++ /dev/null @@ -1,18 +0,0 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens - -import android.media.MediaPlayer -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimerVisitor - -class GetSessionScreen(private val mediaplayer: MediaPlayer?) : FunctionalTimerVisitor { - override fun visitFunctionalCustomTimer(functionalCustomTimer: FunctionalCustomTimer): AbstractSessionScreen = - CustomSessionScreen(functionalCustomTimer, mediaplayer) - - override fun visitFunctionalEndlessTimer(functionalEndlessTimer: FunctionalEndlessTimer): AbstractSessionScreen = - EndlessSessionScreen() - - override fun visitFunctionalBreakTimer(functionalPomodoroTimer: FunctionalPomodoroTimer): AbstractSessionScreen = - BreakSessionScreen(functionalPomodoroTimer, mediaplayer) -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt new file mode 100644 index 0000000..f9c0942 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt @@ -0,0 +1,4 @@ +package be.ugent.sel.studeez.screens.session.sessionScreens + +class SoundPlayer { +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index 89bcc24..57d73e2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -3,7 +3,6 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -14,17 +13,20 @@ import be.ugent.sel.studeez.R import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.screens.session.SessionActions +import be.ugent.sel.studeez.screens.session.sessionScreens.SoundPlayer @Composable fun BreakSessionScreenComposable( open: (String) -> Unit, sessionActions: SessionActions, pomodoroTimer: FunctionalPomodoroTimer, + soundPlayer: SoundPlayer, ) { SessionScreen( open = open, sessionActions = sessionActions, midSection = { Dots(pomodoroTimer = pomodoroTimer) }, + callMediaPlayer = { soundPlayer.playOn(pomodoroTimer.hasCurrentCountdownEnded()) }, motivationString = { motivationString (pomodoroTimer = pomodoroTimer) } ) } @@ -68,9 +70,4 @@ private fun motivationString(pomodoroTimer: FunctionalPomodoroTimer): String { } return resources().getString(R.string.state_focus) -} - -@Composable -private fun test(pomodoroTimer: FunctionalPomodoroTimer): String { - return pomodoroTimer.breaksRemaining.toString() } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt index bafcb19..5cdc62c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -5,15 +5,18 @@ import be.ugent.sel.studeez.R import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.screens.session.SessionActions +import be.ugent.sel.studeez.screens.session.sessionScreens.SoundPlayer @Composable fun CustomTimerSessionScreenComposable( open: (String) -> Unit, sessionActions: SessionActions, - customTimer: FunctionalCustomTimer + customTimer: FunctionalCustomTimer, + soundPlayer: SoundPlayer ) { SessionScreen( open = open, + callMediaPlayer = { soundPlayer.playOn(customTimer.hasEnded()) }, sessionActions = sessionActions ) { motivationString(customTimer = customTimer) From 4a04a703db101141b396f5d52f130bd10951d37f Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 18:58:46 +0200 Subject: [PATCH 127/163] soundplayer wrapper to make mediaplayer easier to work with --- .../studeez/screens/session/SoundPlayer.kt | 29 +++++++++++++++++++ .../session/sessionScreens/SoundPlayer.kt | 4 --- 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt new file mode 100644 index 0000000..14fae19 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SoundPlayer.kt @@ -0,0 +1,29 @@ +package be.ugent.sel.studeez.screens.session + +import android.content.Context +import android.media.MediaPlayer +import android.media.RingtoneManager + +class SoundPlayer(private val context: Context) { + + var oldValue: Boolean = false + var mediaPlayer: MediaPlayer = initPlayer() + + fun playOn(newValue: Boolean) { + if (oldValue != newValue) { + mediaPlayer.start() + mediaPlayer.setOnCompletionListener { + mediaPlayer = initPlayer() + } + oldValue = newValue + } + } + + + private fun initPlayer(): MediaPlayer { + return MediaPlayer.create( + context, + RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt deleted file mode 100644 index f9c0942..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SoundPlayer.kt +++ /dev/null @@ -1,4 +0,0 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens - -class SoundPlayer { -} \ No newline at end of file From 1db5a4160ea5c84b996b50df79286f291142bd1a Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:00:36 +0200 Subject: [PATCH 128/163] refactor soundplayer location --- .../sessionScreens/composables/BreakTimerScreenComposable.kt | 2 +- .../composables/CustomTimerSessionScreenComposable.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt index 57d73e2..cac3509 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt @@ -13,7 +13,7 @@ import be.ugent.sel.studeez.R import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.screens.session.SessionActions -import be.ugent.sel.studeez.screens.session.sessionScreens.SoundPlayer +import be.ugent.sel.studeez.screens.session.SoundPlayer @Composable fun BreakSessionScreenComposable( diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt index 5cdc62c..ec3c2d8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt @@ -5,7 +5,7 @@ import be.ugent.sel.studeez.R import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer import be.ugent.sel.studeez.resources import be.ugent.sel.studeez.screens.session.SessionActions -import be.ugent.sel.studeez.screens.session.sessionScreens.SoundPlayer +import be.ugent.sel.studeez.screens.session.SoundPlayer @Composable fun CustomTimerSessionScreenComposable( From 47491089928c438ac9861d49da86da91802e0704 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:01:06 +0200 Subject: [PATCH 129/163] soundplayer to screens instead of mediaplayer --- .../composables/GetSessionScreenComposable.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt index bae4d92..3780f42 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt @@ -1,19 +1,15 @@ package be.ugent.sel.studeez.screens.session.sessionScreens.composables -import android.media.MediaPlayer import androidx.compose.runtime.Composable import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimerVisitor import be.ugent.sel.studeez.screens.session.SessionActions -import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen -import be.ugent.sel.studeez.screens.session.sessionScreens.BreakSessionScreen -import be.ugent.sel.studeez.screens.session.sessionScreens.CustomSessionScreen -import be.ugent.sel.studeez.screens.session.sessionScreens.EndlessSessionScreen +import be.ugent.sel.studeez.screens.session.SoundPlayer class GetSessionScreenComposable( - private val mediaplayer: MediaPlayer?, + private val soundPlayer: SoundPlayer, private val open: (String) -> Unit, private val sessionActions: SessionActions ) : @@ -23,7 +19,8 @@ class GetSessionScreenComposable( return { CustomTimerSessionScreenComposable( open = open, sessionActions = sessionActions, - customTimer = functionalCustomTimer + soundPlayer = soundPlayer, + customTimer = functionalCustomTimer, ) } } @@ -42,6 +39,7 @@ class GetSessionScreenComposable( BreakSessionScreenComposable( open = open, sessionActions = sessionActions, + soundPlayer = soundPlayer, pomodoroTimer = functionalPomodoroTimer ) } From 8294d63e92d9c7445f7361077cde75f24581cb42 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:02:23 +0200 Subject: [PATCH 130/163] added callmedia to session screen --- .../sessionScreens/composables/SessionScreenComposable.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt index b31ee45..0d02add 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt @@ -16,13 +16,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer import be.ugent.sel.studeez.screens.session.SessionActions @Composable fun SessionScreen( open: (String) -> Unit, sessionActions: SessionActions, + callMediaPlayer: () -> Unit = {}, midSection: @Composable () -> Int = {0}, motivationString: @Composable () -> String, @@ -32,6 +32,7 @@ fun SessionScreen( ) { Timer( sessionActions = sessionActions, + callMediaPlayer = callMediaPlayer, motivationString = motivationString, MidSection = midSection ) @@ -49,7 +50,6 @@ fun SessionScreen( fun EndSessionButton(sessionActions: SessionActions) { TextButton( onClick = { - sessionActions.releaseMediaPlayer sessionActions.endSession() }, modifier = Modifier From 53bdf7d21588a5f5199a3861d807a447343ab2f5 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:02:55 +0200 Subject: [PATCH 131/163] added callMediaPlayer call --- .../session/sessionScreens/composables/TimerComposable.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt index 87a6839..cade06f 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt @@ -23,6 +23,7 @@ import kotlin.time.Duration.Companion.seconds @Composable fun Timer( sessionActions: SessionActions, + callMediaPlayer: () -> Unit, motivationString: @Composable () -> String, MidSection: @Composable () -> Int ) { @@ -30,7 +31,7 @@ fun Timer( LaunchedEffect(tikker) { delay(1.seconds) sessionActions.getTimer().tick() - // callMediaPlayer() TODO + callMediaPlayer() tikker = !tikker } From 346b24aabe952a0d46f4b5f537901a71771631d6 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:05:43 +0200 Subject: [PATCH 132/163] give soundplayer to visitor --- .../studeez/screens/session/SessionRoute.kt | 14 +- .../sessionScreens/AbstractSessionScreen.kt | 150 ------------------ .../sessionScreens/BreakSessionScreen.kt | 93 ----------- .../sessionScreens/CustomSessionScreen.kt | 35 ---- .../sessionScreens/EndlessSessionScreen.kt | 16 -- 5 files changed, 3 insertions(+), 305 deletions(-) delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt delete mode 100644 app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt index 959bc74..3572b5e 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt @@ -5,32 +5,23 @@ import android.media.RingtoneManager import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer -import be.ugent.sel.studeez.screens.session.sessionScreens.AbstractSessionScreen -import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreen -import be.ugent.sel.studeez.screens.session.sessionScreens.composables.BreakSessionScreenComposable import be.ugent.sel.studeez.screens.session.sessionScreens.composables.GetSessionScreenComposable data class SessionActions( val getTimer: () -> FunctionalTimer, val getTask: () -> String, - val startMediaPlayer: () -> Unit, - val releaseMediaPlayer: () -> Unit, val endSession: () -> Unit ) private fun getSessionActions( viewModel: SessionViewModel, openAndPopUp: (String, String) -> Unit, - mediaplayer: MediaPlayer, ): SessionActions { return SessionActions( getTimer = viewModel::getTimer, getTask = viewModel::getTask, endSession = { viewModel.endSession(openAndPopUp) }, - startMediaPlayer = mediaplayer::start, - releaseMediaPlayer = mediaplayer::release, ) } @@ -50,8 +41,9 @@ fun SessionRoute( mediaplayer = mediaplayer ) - val sessionActions = getSessionActions(viewModel, openAndPopUp, mediaplayer) - val sessionScreen = viewModel.getTimer().accept(GetSessionScreenComposable(mediaplayer, open, sessionActions)) + val soundPlayer = SoundPlayer(LocalContext.current) + val sessionActions = getSessionActions(viewModel, openAndPopUp) + val sessionScreen = viewModel.getTimer().accept(GetSessionScreenComposable(soundPlayer, open, sessionActions)) sessionScreen() } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt deleted file mode 100644 index 08a8a72..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/AbstractSessionScreen.kt +++ /dev/null @@ -1,150 +0,0 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.material.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer -import be.ugent.sel.studeez.screens.session.SessionActions -import kotlinx.coroutines.delay -import kotlin.time.Duration.Companion.seconds - -abstract class AbstractSessionScreen { - - @Composable - operator fun invoke( - open: (String) -> Unit, - sessionActions: SessionActions, - ) { - Column( - modifier = Modifier.padding(10.dp) - ) { - Timer( - sessionActions = sessionActions, - ) - Box( - contentAlignment = Alignment.Center, modifier = Modifier - .fillMaxWidth() - .padding(50.dp) - ) { - TextButton( - onClick = { - sessionActions.releaseMediaPlayer - sessionActions.endSession() - }, - modifier = Modifier - .padding(horizontal = 20.dp) - .border(1.dp, Color.Red, RoundedCornerShape(32.dp)) - .background(Color.Transparent) - ) { - Text( - text = "End session", - color = Color.Red, - fontWeight = FontWeight.Bold, - fontSize = 18.sp, - modifier = Modifier.padding(1.dp) - ) - } - } - } - } - - @Composable - fun Timer( - sessionActions: SessionActions, - ) { - var tikker by remember { mutableStateOf(false) } - LaunchedEffect(tikker) { - delay(1.seconds) - sessionActions.getTimer().tick() - callMediaPlayer() - tikker = !tikker - } - - val hms = sessionActions.getTimer().getHoursMinutesSeconds() - Column { - Text( - text = hms.toString(), - modifier = Modifier - .fillMaxWidth() - .padding(50.dp), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - fontSize = 40.sp, - ) - - Text( - text = motivationString(), - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Light, - fontSize = 30.sp - ) - - MidSection() - - Box( - contentAlignment = Alignment.Center, modifier = Modifier - .fillMaxWidth() - .padding(50.dp) - ) { - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .padding(16.dp) - .background(Color.Blue, RoundedCornerShape(32.dp)) - ) { - Text( - text = sessionActions.getTask(), - color = Color.White, - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(vertical = 4.dp, horizontal = 20.dp) - ) - } - } - } - } - - @Composable - abstract fun motivationString(): String - - @Composable - open fun MidSection() { - // Default has no midsection, unless overwritten. - } - - abstract fun callMediaPlayer() - -} - -@Preview -@Composable -fun TimerPreview() { - val sessionScreen = object : AbstractSessionScreen() { - @Composable - override fun motivationString(): String = "Test" - override fun callMediaPlayer() {} - - } - sessionScreen.Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {}, {})) -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt deleted file mode 100644 index 9c59b46..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakSessionScreen.kt +++ /dev/null @@ -1,93 +0,0 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens - -import android.media.MediaPlayer -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import be.ugent.sel.studeez.R -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer -import be.ugent.sel.studeez.resources -import be.ugent.sel.studeez.R.string as AppText - -class BreakSessionScreen( - private val funPomoDoroTimer: FunctionalPomodoroTimer, - private var mediaplayer: MediaPlayer? -): AbstractSessionScreen() { - - @Composable - override fun MidSection() { - Dots() - } - - @Composable - fun Dots() { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - ) { - repeat(funPomoDoroTimer.repeats - funPomoDoroTimer.breaksRemaining) { - Dot(color = Color.DarkGray) - } - if (!funPomoDoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) - repeat(funPomoDoroTimer.breaksRemaining - 1) { - Dot(color = Color.Gray) - } - } - } - - @Composable - private fun Dot(color: Color) { - Box(modifier = Modifier - .padding(5.dp) - .size(10.dp) - .clip(CircleShape) - .background(color)) - } - - @Composable - override fun motivationString(): String { - if (funPomoDoroTimer.isInBreak) { - return resources().getString(AppText.state_take_a_break) - } - - if (funPomoDoroTimer.hasEnded()) { - return resources().getString(AppText.state_done) - } - - return resources().getString(AppText.state_focus) - } - - override fun callMediaPlayer() { - if (funPomoDoroTimer.hasEnded()) { - mediaplayer?.let { it: MediaPlayer -> - it.setOnCompletionListener { - it.release() - mediaplayer = null - } - it.start() - } - } else if (funPomoDoroTimer.hasCurrentCountdownEnded()) { - mediaplayer?.start() - } - } -} - -@Preview -@Composable -fun MidsectionPreview() { - val funPomoDoroTimer = FunctionalPomodoroTimer(15, 60, 5) - val breakSessionScreen = BreakSessionScreen(funPomoDoroTimer, MediaPlayer()) - breakSessionScreen.MidSection() -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt deleted file mode 100644 index 7fc60bc..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomSessionScreen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens - -import android.media.MediaPlayer -import androidx.compose.runtime.Composable -import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer -import be.ugent.sel.studeez.resources -import be.ugent.sel.studeez.R.string as AppText - - -class CustomSessionScreen( - private val functionalTimer: FunctionalCustomTimer, - private var mediaplayer: MediaPlayer? -): AbstractSessionScreen() { - - @Composable - override fun motivationString(): String { - if (functionalTimer.hasEnded()) { - return resources().getString(AppText.state_done) - } - return resources().getString(AppText.state_focus) - } - - override fun callMediaPlayer() { - if (functionalTimer.hasEnded()) { - mediaplayer?.let { it: MediaPlayer -> - it.setOnCompletionListener { - it.release() - mediaplayer = null - } - it.start() - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt deleted file mode 100644 index be67cff..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessSessionScreen.kt +++ /dev/null @@ -1,16 +0,0 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens - -import androidx.compose.runtime.Composable -import be.ugent.sel.studeez.resources -import be.ugent.sel.studeez.R.string as AppText - - -class EndlessSessionScreen : AbstractSessionScreen() { - - @Composable - override fun motivationString(): String { - return resources().getString(AppText.state_focus) - } - - override fun callMediaPlayer() {} -} \ No newline at end of file From 9573a2eb5c8b91220ba8784ec80e5275933fec22 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:09:14 +0200 Subject: [PATCH 133/163] changed location of sessionscreens --- .../java/be/ugent/sel/studeez/screens/session/SessionRoute.kt | 2 +- .../{composables => }/BreakTimerScreenComposable.kt | 2 +- .../{composables => }/CustomTimerSessionScreenComposable.kt | 2 +- .../{composables => }/EndlessTimerSessionScreenComposable.kt | 2 +- .../{composables => }/GetSessionScreenComposable.kt | 2 +- .../sessionScreens/{composables => }/SessionScreenComposable.kt | 2 +- .../session/sessionScreens/{composables => }/TimerComposable.kt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/BreakTimerScreenComposable.kt (96%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/CustomTimerSessionScreenComposable.kt (93%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/EndlessTimerSessionScreenComposable.kt (87%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/GetSessionScreenComposable.kt (96%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/SessionScreenComposable.kt (96%) rename app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/{composables => }/TimerComposable.kt (97%) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt index 3572b5e..0db1e73 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt @@ -6,7 +6,7 @@ import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer -import be.ugent.sel.studeez.screens.session.sessionScreens.composables.GetSessionScreenComposable +import be.ugent.sel.studeez.screens.session.sessionScreens.GetSessionScreenComposable data class SessionActions( val getTimer: () -> FunctionalTimer, diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt similarity index 96% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt index cac3509..093c16a 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.foundation.background import androidx.compose.foundation.layout.* diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt similarity index 93% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt index ec3c2d8..a0c385c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/CustomTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/CustomTimerSessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.runtime.Composable import be.ugent.sel.studeez.R diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt similarity index 87% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt index b223f52..4f1dbe3 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/EndlessTimerSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/EndlessTimerSessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.runtime.Composable import be.ugent.sel.studeez.R diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt similarity index 96% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt index 3780f42..47ca52e 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/GetSessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/GetSessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.runtime.Composable import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt similarity index 96% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt index 0d02add..c94d2a5 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/SessionScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/SessionScreenComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.foundation.background import androidx.compose.foundation.border diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt similarity index 97% rename from app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt index cade06f..2a29403 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/composables/TimerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/TimerComposable.kt @@ -1,4 +1,4 @@ -package be.ugent.sel.studeez.screens.session.sessionScreens.composables +package be.ugent.sel.studeez.screens.session.sessionScreens import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box From 0487347c8f4882c51afa8bf0b6b64552f943c5d0 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:18:47 +0200 Subject: [PATCH 134/163] cleanup sessionroute --- .../studeez/screens/session/InvisibleSessionManager.kt | 10 ++++++++-- .../ugent/sel/studeez/screens/session/SessionRoute.kt | 9 +-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt index 9051fa8..763fb1d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/InvisibleSessionManager.kt @@ -1,6 +1,10 @@ package be.ugent.sel.studeez.screens.session +import android.annotation.SuppressLint +import android.content.Context import android.media.MediaPlayer +import android.media.RingtoneManager +import android.net.Uri import kotlinx.coroutines.delay import javax.inject.Singleton import kotlin.time.Duration.Companion.seconds @@ -10,9 +14,11 @@ object InvisibleSessionManager { private var viewModel: SessionViewModel? = null private lateinit var mediaPlayer: MediaPlayer - fun setParameters(viewModel: SessionViewModel, mediaplayer: MediaPlayer) { + fun setParameters(viewModel: SessionViewModel, context: Context) { + val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) + this.mediaPlayer = MediaPlayer.create(context, uri) + this.mediaPlayer.isLooping = false this.viewModel = viewModel - this.mediaPlayer = mediaplayer } suspend fun updateTimer() { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt index 0db1e73..9d1953b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt @@ -31,15 +31,8 @@ fun SessionRoute( openAndPopUp: (String, String) -> Unit, viewModel: SessionViewModel, ) { - val context = LocalContext.current - val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) - val mediaplayer = MediaPlayer.create(context, uri) - mediaplayer.isLooping = false - InvisibleSessionManager.setParameters( - viewModel = viewModel, - mediaplayer = mediaplayer - ) + InvisibleSessionManager.setParameters(viewModel = viewModel, context = LocalContext.current) val soundPlayer = SoundPlayer(LocalContext.current) val sessionActions = getSessionActions(viewModel, openAndPopUp) From 6554a92f837285b43e200e7face8cdbd3e348671 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 19:20:42 +0200 Subject: [PATCH 135/163] remove imports sessionroute --- .../java/be/ugent/sel/studeez/screens/session/SessionRoute.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt index 9d1953b..aeaf544 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionRoute.kt @@ -1,8 +1,5 @@ package be.ugent.sel.studeez.screens.session -import android.media.MediaPlayer -import android.media.RingtoneManager -import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer From 9ea4f73187835fe465f9c1db5d409e192d7aa65e Mon Sep 17 00:00:00 2001 From: brreynie Date: Mon, 15 May 2023 20:54:47 +0200 Subject: [PATCH 136/163] i18n improvement --- .../screens/session_recap/SessionRecapScreen.kt | 11 ++++++++--- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt index f3a5739..73f4100 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session_recap/SessionRecapScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel import be.ugent.sel.studeez.R import be.ugent.sel.studeez.common.composable.BasicButton import be.ugent.sel.studeez.common.composable.ImageBackgroundButton @@ -70,7 +69,7 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi verticalArrangement = Arrangement.SpaceBetween ) { Text( - text = stringResource(R.string.congrats) + hms, + text = stringResource(R.string.congrats, hms), modifier = Modifier .fillMaxWidth(), textAlign = TextAlign.Center, @@ -135,6 +134,12 @@ fun SessionRecapScreen(modifier: Modifier, sessionRecapActions: SessionRecapActi fun SessionRecapScreenPreview() { SessionRecapScreen( modifier = Modifier, - sessionRecapActions = SessionRecapActions(hiltViewModel(), {}, {}) + sessionRecapActions = SessionRecapActions( + { SessionReport( + studyTime = 100, + ) }, + {}, + {}, + ) ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index be6b7ef..58e952d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -150,7 +150,7 @@ Number of Repeats - "Congratulations! You studied: " + "Congratulations! You studied: %s" How did it go? Good Bad From 5ddd92a66fd6862b1755eb50fba70c25206ab721 Mon Sep 17 00:00:00 2001 From: lbarraga Date: Mon, 15 May 2023 20:58:37 +0200 Subject: [PATCH 137/163] 'merge' dots --- .../FunctionalPomodoroTimer.kt | 2 +- .../BreakTimerScreenComposable.kt | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt index 765fbcd..e754963 100644 --- a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt @@ -6,7 +6,7 @@ class FunctionalPomodoroTimer( val repeats: Int ) : FunctionalTimer(studyTime) { - var breaksRemaining = repeats + var breaksRemaining = repeats - 1 var isInBreak = false override fun tick() { diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt index 093c16a..42ec4f7 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/sessionScreens/BreakTimerScreenComposable.kt @@ -38,12 +38,18 @@ private fun Dots(pomodoroTimer: FunctionalPomodoroTimer): Int { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { - repeat(pomodoroTimer.repeats - pomodoroTimer.breaksRemaining) { - Dot(color = Color.DarkGray) - } - if (!pomodoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) - repeat(pomodoroTimer.breaksRemaining - 1) { - Dot(color = Color.Gray) + if (pomodoroTimer.hasEnded()) { + repeat(pomodoroTimer.repeats) { + Dot(Color.Green) + } + } else { + repeat(pomodoroTimer.repeats - pomodoroTimer.breaksRemaining - 1) { + Dot(color = Color.DarkGray) + } + if (!pomodoroTimer.isInBreak) Dot(Color.Green) else Dot(Color.DarkGray) + repeat(pomodoroTimer.breaksRemaining) { + Dot(color = Color.Gray) + } } } return pomodoroTimer.breaksRemaining From 860fb5fac190bb86e35c86ef0e7c30335542bf7f Mon Sep 17 00:00:00 2001 From: Rune Dyselinck Date: Mon, 15 May 2023 21:53:04 +0200 Subject: [PATCH 138/163] ui tests --- .idea/androidTestResultsUserPreferences.xml | 338 ++++++++++++++++++ app/build.gradle | 2 +- .../java/be/ugent/sel/studeez/FabTest.kt | 74 ++++ .../be/ugent/sel/studeez/HomeScreenTest.kt | 173 ++++++++- .../be/ugent/sel/studeez/LoginScreenTest.kt | 68 ++++ .../sel/studeez/ProfileEditScreenTest.kt | 68 ++++ .../be/ugent/sel/studeez/ProfileScreenTest.kt | 42 +++ .../sel/studeez/SessionRecapScreenTest.kt | 75 ++++ .../be/ugent/sel/studeez/SessionScreenTest.kt | 123 +++++-- .../be/ugent/sel/studeez/SignUpScreenTest.kt | 52 +++ .../be/ugent/sel/studeez/SplashScreenTest.kt | 40 +++ .../be/ugent/sel/studeez/SubjectScreenTest.kt | 155 ++++++++ .../be/ugent/sel/studeez/TaskScreenTest.kt | 160 +++++++++ .../sel/studeez/TimerOverviewScreenTest.kt | 58 +++ .../be/ugent/sel/studeez/TimerScreenTest.kt | 37 ++ .../sel/studeez/TimerSelectionScreenTest.kt | 40 +++ .../screens/timer_form/TimerFormRoute.kt | 13 + .../TimerTypeSelectScreen.kt | 10 + .../InvisibleSessionManagerTest.kt | 7 +- build.gradle | 6 +- 20 files changed, 1492 insertions(+), 49 deletions(-) create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/FabTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/LoginScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/ProfileEditScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/ProfileScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SessionRecapScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SignUpScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SplashScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/SubjectScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TaskScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TimerOverviewScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TimerScreenTest.kt create mode 100644 app/src/androidTest/java/be/ugent/sel/studeez/TimerSelectionScreenTest.kt diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index 59f6bc1..c9ff4ce 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -3,6 +3,19 @@