Skip to content

Commit

Permalink
✨ Add view for current check-in (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
jheubuch authored Aug 5, 2024
1 parent fa850b7 commit 6bf225e
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 53 deletions.
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

0 comments on commit 6bf225e

Please sign in to comment.