Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add view for current check-in #401

Merged
merged 3 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/src/main/kotlin/de/hbch/traewelling/api/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ interface StatisticsService {
}

interface CheckInService {
@GET("user/statuses/active")
suspend fun getOwnActiveStatus(): Response<Data<Status>>

@GET("dashboard")
fun getPersonalDashboard(
@Query("page") page: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class LogInterceptor : Interceptor {
val copy = request.newBuilder().build()
val response = chain.proceed(request)

val ignoredCodes = listOf(401, 409)
val ignoredCodes = listOf(401, 409, 404)
val path = request.url.encodedPath

if (!response.isSuccessful && !ignoredCodes.contains(response.code)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import de.hbch.traewelling.api.TraewellingApi
import de.hbch.traewelling.api.WebhookRelayApi
import de.hbch.traewelling.api.models.Data
import de.hbch.traewelling.api.models.station.Station
import de.hbch.traewelling.api.models.status.Status
import de.hbch.traewelling.api.models.status.StatusVisibility
import de.hbch.traewelling.api.models.user.User
import de.hbch.traewelling.ui.login.LoginActivity
Expand Down Expand Up @@ -41,6 +42,8 @@ class LoggedInUserViewModel : ViewModel() {
val defaultStatusVisibility: StatusVisibility
get() = _user.value?.defaultStatusVisibility ?: StatusVisibility.PUBLIC

val currentStatus = MutableLiveData<Status?>(null)

fun setHomelandStation(station: Station) {
_user.value?.home = station
}
Expand Down Expand Up @@ -174,4 +177,13 @@ class LoggedInUserViewModel : ViewModel() {
}
})
}

suspend fun updateCurrentStatus() {
val response = TraewellingApi.checkInService.getOwnActiveStatus()
if (response.code() != 404) {
currentStatus.postValue(response.body()?.data)
} else {
currentStatus.postValue(null)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -26,6 +27,7 @@ import de.hbch.traewelling.ui.include.cardSearchStation.CardSearch
import de.hbch.traewelling.ui.include.status.CheckInCardViewModel
import de.hbch.traewelling.util.OnBottomReached
import de.hbch.traewelling.util.checkInList
import kotlinx.coroutines.launch
import java.time.ZonedDateTime

@OptIn(ExperimentalMaterialApi::class)
Expand All @@ -45,6 +47,7 @@ fun Dashboard(
val checkInCardViewModel : CheckInCardViewModel = viewModel()
val refreshing by dashboardViewModel.isRefreshing.observeAsState(false)
val checkIns = remember { dashboardViewModel.checkIns }
val coroutineScope = rememberCoroutineScope()
var currentPage by remember { mutableIntStateOf(1) }
val pullRefreshState = rememberPullRefreshState(
refreshing = refreshing,
Expand All @@ -61,6 +64,9 @@ fun Dashboard(
} else {
loggedInUserViewModel.getLoggedInUser()
loggedInUserViewModel.getLastVisitedStations { }
coroutineScope.launch {
loggedInUserViewModel.updateCurrentStatus()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fun CardSearch(
style = AppTypography.headlineLarge,
modifier = Modifier.padding(8.dp),
fontFamily = Twindexx,
color = LocalColorScheme.current.onPrimaryContainer
color = LocalColorScheme.current.primary
)
Search(
homelandStation = homelandStation,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package de.hbch.traewelling.ui.include.status

import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
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.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import de.hbch.traewelling.R
import de.hbch.traewelling.api.models.status.Status
import de.hbch.traewelling.ui.composables.LineIcon
import de.hbch.traewelling.ui.user.getDurationString
import kotlinx.coroutines.delay
import java.time.Duration
import java.time.ZonedDateTime
import kotlin.math.absoluteValue

@Composable
fun ActiveStatusBar(
status: Status?,
modifier: Modifier = Modifier
) {
if (status != null) {
var progress by remember { mutableFloatStateOf(0f) }
val progressAnimation by animateFloatAsState(
targetValue = progress,
animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing),
label = "AnimateActiveStatusBarProgress",
)
var duration by remember { mutableIntStateOf(0) }

LaunchedEffect(true) {
while (true) {
progress = calculateProgress(
from = status.journey.departureManual ?: status.journey.origin.departureReal ?: status.journey.origin.departurePlanned,
to = status.journey.destination.arrivalReal ?: status.journey.destination.arrivalPlanned
)
duration = Duration.between(
status.journey.destination.arrivalReal ?: status.journey.destination.arrivalPlanned,
ZonedDateTime.now()
).toMinutes().absoluteValue.toInt()
delay(5000)
}
}
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Row(
modifier = modifier.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
LineIcon(
lineName = status.journey.line,
journeyNumber = null,
lineId = status.journey.lineId,
operatorCode = status.journey.operator?.id
)
Icon(
painter = painterResource(id = R.drawable.ic_arrow_right),
contentDescription = null
)
Text(
text = status.journey.destination.name
)
}
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
if (duration > 0) {
Text(
text = stringResource(id = R.string.time_left, getDurationString(duration))
)
}
Box(
modifier = Modifier
.background(Color.Blue)
.padding(4.dp)
) {
Text(
text = stringResource(id = R.string.platform,
status.journey.destination.arrivalPlatformReal
?: status.journey.destination.arrivalPlatformPlanned ?: ""),
color = Color.White
)
}
}
}
LinearProgressIndicator(
progress = { progressAnimation },
modifier = Modifier.fillMaxWidth()
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import android.icu.text.MeasureFormat
import android.icu.util.Measure
import android.icu.util.MeasureUnit
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -28,8 +31,10 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -73,6 +78,7 @@ import de.hbch.traewelling.ui.user.getDurationString
import de.hbch.traewelling.util.getLocalDateTimeString
import de.hbch.traewelling.util.getLocalTimeString
import de.hbch.traewelling.util.shareStatus
import kotlinx.coroutines.delay
import java.time.Duration
import java.time.ZonedDateTime
import java.util.Locale
Expand All @@ -97,6 +103,25 @@ fun CheckInCard(
val statusClickedAction: () -> Unit = {
statusSelected(status.id)
}

var progress by remember { mutableFloatStateOf(0f) }
val progressAnimation by animateFloatAsState(
targetValue = progress,
animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing),
label = "AnimateCheckInCardProgress",
)
LaunchedEffect(true) {
while (true) {
progress = calculateProgress(
from = status.journey.departureManual ?: status.journey.origin.departureReal
?: status.journey.origin.departurePlanned,
to = status.journey.arrivalManual ?: status.journey.destination.arrivalReal
?: status.journey.destination.arrivalPlanned
)
delay(5000)
}
}

ElevatedCard(
modifier = modifier.fillMaxWidth(),
onClick = statusClickedAction
Expand Down Expand Up @@ -209,13 +234,9 @@ fun CheckInCard(
textClicked = statusClickedAction
)
}
val progress = calculateProgress(
from = status.journey.departureManual ?: status.journey.origin.departureReal ?: status.journey.origin.departurePlanned,
to = status.journey.arrivalManual ?: status.journey.destination.arrivalReal ?: status.journey.destination.arrivalPlanned
)
LinearProgressIndicator(
progress = {
if (progress.isNaN()) 1f else progress
if (progressAnimation.isNaN()) 1f else progressAnimation
},
modifier = Modifier
.fillMaxWidth()
Expand Down Expand Up @@ -245,8 +266,7 @@ fun CheckInCard(
}
}

@Composable
private fun calculateProgress(
fun calculateProgress(
from: ZonedDateTime,
to: ZonedDateTime
): Float {
Expand Down
Loading
Loading