Merge pull request #106 from SELab1/feed

Feed
This commit is contained in:
brreynie 2023-05-09 23:16:23 +02:00 committed by GitHub Enterprise
commit e8c073039e
12 changed files with 143 additions and 49 deletions

View file

@ -2,6 +2,7 @@ package be.ugent.sel.studeez.common.composable
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -20,6 +21,7 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -48,6 +50,7 @@ fun BasicButton(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
colors: ButtonColors = ButtonDefaults.buttonColors(), colors: ButtonColors = ButtonDefaults.buttonColors(),
border: BorderStroke? = null, border: BorderStroke? = null,
enabled: Boolean = true,
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
Button( Button(
@ -56,6 +59,7 @@ fun BasicButton(
shape = defaultButtonShape(), shape = defaultButtonShape(),
colors = colors, colors = colors,
border = border, border = border,
enabled = enabled,
) { ) {
Text( Text(
text = stringResource(text), text = stringResource(text),
@ -74,17 +78,22 @@ fun BasicButtonPreview() {
fun StealthButton( fun StealthButton(
@StringRes text: Int, @StringRes text: Int,
modifier: Modifier = Modifier.card(), modifier: Modifier = Modifier.card(),
enabled: Boolean = true,
onClick: () -> Unit, 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( BasicButton(
text = text, text = text,
onClick = onClick, onClick = onClick,
modifier = modifier, modifier = modifier,
enabled = enabled,
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.surface, backgroundColor = MaterialTheme.colors.surface,
contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f) contentColor = borderColor
), ),
border = BorderStroke(3.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.4f)) border = BorderStroke(2.dp, borderColor)
) )
} }

View file

@ -9,21 +9,47 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import be.ugent.sel.studeez.common.composable.BasicTextButton
import be.ugent.sel.studeez.common.composable.DateText 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.FeedEntry
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
import be.ugent.sel.studeez.R.string as AppText
@Composable @Composable
fun Feed( fun Feed(
uiState: FeedUiState, uiState: FeedUiState,
continueTask: (String, String) -> Unit, continueTask: (String, String) -> Unit,
onEmptyFeedHelp: () -> Unit
) { ) {
when (uiState) { when (uiState) {
FeedUiState.Loading -> { 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( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -33,8 +59,13 @@ fun Feed(
) { ) {
CircularProgressIndicator(color = MaterialTheme.colors.onBackground) CircularProgressIndicator(color = MaterialTheme.colors.onBackground)
} }
} }
is FeedUiState.Succes -> {
@Composable
fun FeedWithElements(
uiState: FeedUiState.Succes,
continueTask: (String, String) -> Unit,
) {
val feedEntries = uiState.feedEntries val feedEntries = uiState.feedEntries
LazyColumn { LazyColumn {
items(feedEntries.toList()) { (date, feedEntries) -> items(feedEntries.toList()) { (date, feedEntries) ->
@ -61,6 +92,22 @@ fun Feed(
Spacer(modifier = Modifier.height(20.dp)) 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() { fun FeedLoadingPreview() {
Feed( Feed(
uiState = FeedUiState.Loading, uiState = FeedUiState.Loading,
continueTask = { _, _ -> run {} }, continueTask = { _, _ -> run {} }, {}
) )
} }
@ -108,6 +155,6 @@ fun FeedPreview() {
) )
) )
), ),
continueTask = { _, _ -> run {} }, continueTask = { _, _ -> run {} }, {}
) )
} }

View file

@ -18,6 +18,7 @@ import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.StealthButton 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.FeedEntry
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
import be.ugent.sel.studeez.R.string as AppText
@Composable @Composable
fun FeedEntry( fun FeedEntry(
@ -69,15 +70,20 @@ fun FeedEntry(
Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString()) Text(text = HoursMinutesSeconds(feedEntry.totalStudyTime).toString())
} }
} }
val buttonText: Int = if (feedEntry.isArchived) AppText.deleted else AppText.continue_task
StealthButton( StealthButton(
text = R.string.continue_task, text = buttonText,
enabled = !feedEntry.isArchived,
modifier = Modifier modifier = Modifier
.padding(start = 10.dp, end = 5.dp) .padding(start = 10.dp, end = 5.dp)
.weight(6f) .weight(6f)
) { ) {
if (!feedEntry.isArchived) {
continueWithTask() continueWithTask()
} }
} }
}
} }
} }

View file

@ -38,4 +38,8 @@ class FeedViewModel @Inject constructor(
open(StudeezDestinations.TIMER_SELECTION_SCREEN) open(StudeezDestinations.TIMER_SELECTION_SCREEN)
} }
} }
fun onEmptyFeedHelp(open: (String) -> Unit) {
open(StudeezDestinations.ADD_SUBJECT_FORM)
}
} }

View file

@ -1,14 +1,25 @@
package be.ugent.sel.studeez.common.ext 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.foundation.layout.*
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
fun Modifier.textButton(): Modifier { fun Modifier.textButton(): Modifier {
return this.fillMaxWidth().padding(16.dp, 8.dp, 16.dp, 0.dp) 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 { fun Modifier.basicButton(): Modifier {
return this.fillMaxWidth().padding(16.dp, 8.dp) return this.fillMaxWidth().padding(16.dp, 8.dp)
} }

View file

@ -9,5 +9,6 @@ data class FeedEntry(
val taskId: String = "", // Name of task is not unique val taskId: String = "", // Name of task is not unique
val subjectId: String = "", val subjectId: String = "",
val totalStudyTime: Int = 0, val totalStudyTime: Int = 0,
val endTime: Timestamp = Timestamp(0, 0) val endTime: Timestamp = Timestamp(0, 0),
val isArchived: Boolean = false
) )

View file

@ -74,7 +74,8 @@ class FirebaseFeedDAO @Inject constructor(
taskId = task.id, taskId = task.id,
subjectId = subject.id, subjectId = subject.id,
totalStudyTime = sessionReport.studyTime, totalStudyTime = sessionReport.studyTime,
endTime = sessionReport.endTime endTime = sessionReport.endTime,
isArchived = task.archived || subject.archived
) )
} }
} }

View file

@ -32,6 +32,7 @@ fun HomeRoute(
navigationBarActions = navigationBarActions, navigationBarActions = navigationBarActions,
feedUiState = feedUiState, feedUiState = feedUiState,
continueTask = { subjectId, taskId -> feedViewModel.continueTask(open, subjectId, taskId) }, continueTask = { subjectId, taskId -> feedViewModel.continueTask(open, subjectId, taskId) },
onEmptyFeedHelp = { feedViewModel.onEmptyFeedHelp(open) }
) )
} }
@ -42,6 +43,7 @@ fun HomeScreen(
navigationBarActions: NavigationBarActions, navigationBarActions: NavigationBarActions,
feedUiState: FeedUiState, feedUiState: FeedUiState,
continueTask: (String, String) -> Unit, continueTask: (String, String) -> Unit,
onEmptyFeedHelp: () -> Unit,
) { ) {
PrimaryScreenTemplate( PrimaryScreenTemplate(
title = resources().getString(R.string.home), title = resources().getString(R.string.home),
@ -49,7 +51,7 @@ fun HomeScreen(
navigationBarActions = navigationBarActions, navigationBarActions = navigationBarActions,
// TODO barAction = { FriendsAction() } // TODO barAction = { FriendsAction() }
) { ) {
Feed(feedUiState, continueTask) Feed(feedUiState, continueTask, onEmptyFeedHelp)
} }
} }
@ -101,5 +103,6 @@ fun HomeScreenPreview() {
) )
), ),
continueTask = { _, _ -> run {} }, continueTask = { _, _ -> run {} },
onEmptyFeedHelp = {}
) )
} }

View file

@ -82,12 +82,13 @@ fun TimerOverviewScreen(
items(timers.value) { timerInfo -> items(timers.value) { timerInfo ->
TimerEntry( TimerEntry(
timerInfo = timerInfo, timerInfo = timerInfo,
) { rightButton = {
StealthButton( StealthButton(
text = R.string.edit, text = R.string.edit,
onClick = { timerOverviewActions.onEditClick(timerInfo) } onClick = { timerOverviewActions.onEditClick(timerInfo) }
) )
} }
)
} }

View file

@ -1,10 +1,13 @@
package be.ugent.sel.studeez.screens.timer_selection package be.ugent.sel.studeez.screens.timer_selection
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.R import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
import be.ugent.sel.studeez.common.composable.StealthButton import be.ugent.sel.studeez.common.composable.StealthButton
@ -99,7 +102,10 @@ fun CustomTimerEntry(
) )
}, },
rightButton = { rightButton = {
TimePickerButton(initialSeconds = hms.getTotalSeconds()) { chosenTime -> TimePickerButton(
initialSeconds = hms.getTotalSeconds(),
modifier = Modifier.padding(horizontal = 5.dp)
) { chosenTime ->
timerInfo.studyTime = chosenTime timerInfo.studyTime = chosenTime
} }
} }

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import be.ugent.sel.studeez.data.SelectedTimerState import be.ugent.sel.studeez.data.SelectedTimerState
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.TimerDAO import be.ugent.sel.studeez.domain.TimerDAO
@ -21,7 +22,9 @@ class TimerSelectionViewModel @Inject constructor(
logService: LogService logService: LogService
) : StudeezViewModel(logService) { ) : StudeezViewModel(logService) {
var customTimerStudyTime: MutableState<Int> = mutableStateOf(0) var customTimerStudyTime: MutableState<Int> = mutableStateOf(
HoursMinutesSeconds(1, 0, 0).getTotalSeconds()
)
fun getAllTimers() : Flow<List<TimerInfo>> { fun getAllTimers() : Flow<List<TimerInfo>> {
return timerDAO.getAllTimers() return timerDAO.getAllTimers()

View file

@ -31,6 +31,8 @@
<!-- Feed--> <!-- Feed-->
<string name="continue_task">Continue</string> <string name="continue_task">Continue</string>
<string name="your_feed">This is your feed</string>
<string name="empty_feed_help_text">Create you first subject and tasks to get started</string>
<!-- Tasks --> <!-- Tasks -->
<string name="tasks">Tasks</string> <string name="tasks">Tasks</string>