diff --git a/app/src/main/kotlin/de/hbch/traewelling/api/ApiService.kt b/app/src/main/kotlin/de/hbch/traewelling/api/ApiService.kt index ae53460e..d31c8271 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/api/ApiService.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/api/ApiService.kt @@ -69,10 +69,10 @@ interface AuthService { @GET("auth/user") fun getLoggedInUser(): Call> - @PUT("trains/station/{stationName}/home") - fun setUserHomelandStation( - @Path("stationName") stationName: String - ): Call> + @PUT("station/{id}/home") + suspend fun setUserHomelandStation( + @Path("id") stationId: Int + ): Data @GET("trains/station/history") fun getLastVisitedStations(): Call>> @@ -189,12 +189,12 @@ interface TravelService { @Query("longitude") longitude: Double ): Data - @GET("trains/station/{station}/departures") - fun getDeparturesAtStation( - @Path("station", encoded = false) station: String, + @GET("station/{id}/departures") + suspend fun getDeparturesAtStation( + @Path("id") stationId: Int, @Query("when") time: ZonedDateTime, @Query("travelType") filter: String - ): Call + ): HafasTripPage @GET("trains/station/autocomplete/{station}") suspend fun autoCompleteStationSearch( diff --git a/app/src/main/kotlin/de/hbch/traewelling/api/models/station/Station.kt b/app/src/main/kotlin/de/hbch/traewelling/api/models/station/Station.kt index 09eb7a70..34bc0dc4 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/api/models/station/Station.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/api/models/station/Station.kt @@ -2,10 +2,6 @@ package de.hbch.traewelling.api.models.station import com.google.gson.annotations.SerializedName -data class StationData( - @SerializedName("data") val data: Station -) - data class Station( @SerializedName("id") val id: Int, @SerializedName("name") val name: String, diff --git a/app/src/main/kotlin/de/hbch/traewelling/navigation/Destinations.kt b/app/src/main/kotlin/de/hbch/traewelling/navigation/Destinations.kt index 27546e98..4845cab2 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/navigation/Destinations.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/navigation/Destinations.kt @@ -95,7 +95,7 @@ object SearchConnection : ArgumentDestination, DeepLinkedDestination { override val route = "search-connection/?station={station}&date={date}" override val arguments = listOf( navArgument("station") { - type = NavType.StringType + type = NavType.IntType }, navArgument("date") { type = NavType.LongType diff --git a/app/src/main/kotlin/de/hbch/traewelling/navigation/NavHost.kt b/app/src/main/kotlin/de/hbch/traewelling/navigation/NavHost.kt index 2817b97a..c3e9214a 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/navigation/NavHost.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/navigation/NavHost.kt @@ -66,7 +66,7 @@ fun TraewelldroidNavHost( val context = LocalContext.current val secureStorage = SecureStorage(context) - val navToSearchConnections: (String, ZonedDateTime?) -> Unit = { station, date -> + val navToSearchConnections: (Int, ZonedDateTime?) -> Unit = { station, date -> val formattedDate = if (date == null) "" @@ -366,7 +366,7 @@ fun TraewelldroidNavHost( SearchConnection( loggedInUserViewModel = loggedInUserViewModel, - station = it.arguments?.getString("station") ?: "", + station = it.arguments?.getString("station")?.toIntOrNull() ?: 5167, currentSearchDate = zonedDateTime, checkInViewModel = checkInViewModel, onTripSelected = { diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/dashboard/Dashboard.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/dashboard/Dashboard.kt index afcd7d61..bc576f99 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/dashboard/Dashboard.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/dashboard/Dashboard.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import de.hbch.traewelling.api.models.station.Station import de.hbch.traewelling.api.models.status.Status import de.hbch.traewelling.shared.LoggedInUserViewModel import de.hbch.traewelling.ui.composables.NotificationsAvailableHint @@ -32,7 +33,7 @@ import java.time.ZonedDateTime @Composable fun Dashboard( loggedInUserViewModel: LoggedInUserViewModel, - searchConnectionsAction: (String, ZonedDateTime?) -> Unit = { _, _ -> }, + searchConnectionsAction: (Int, ZonedDateTime?) -> Unit = { _, _ -> }, userSelectedAction: (String) -> Unit = { }, statusSelectedAction: (Int) -> Unit = { }, statusDeletedAction: () -> Unit = { }, diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/include/cardSearchStation/CardSearch.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/include/cardSearchStation/CardSearch.kt index 7ea863bd..5b60feb9 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/include/cardSearchStation/CardSearch.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/include/cardSearchStation/CardSearch.kt @@ -24,7 +24,7 @@ fun CardSearch( modifier: Modifier = Modifier, homelandStationData: LiveData, recentStationsData: LiveData?>, - onStationSelected: (String) -> Unit = { }, + onStationSelected: (Int) -> Unit = { }, onUserSelected: (User) -> Unit = { }, queryStations: Boolean = true, queryUsers: Boolean = true diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/include/status/CheckInCard.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/include/status/CheckInCard.kt index 87e7575c..13c12d99 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/include/status/CheckInCard.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/include/status/CheckInCard.kt @@ -48,8 +48,10 @@ import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import de.hbch.traewelling.R +import de.hbch.traewelling.api.models.station.Station import de.hbch.traewelling.api.models.status.Status import de.hbch.traewelling.api.models.status.StatusBusiness +import de.hbch.traewelling.api.models.trip.HafasTrainTripStation import de.hbch.traewelling.api.models.trip.ProductType import de.hbch.traewelling.shared.LoggedInUserViewModel import de.hbch.traewelling.theme.AppTypography @@ -73,7 +75,7 @@ fun CheckInCard( status: Status?, loggedInUserViewModel: LoggedInUserViewModel? = null, displayLongDate: Boolean = false, - stationSelected: (String, ZonedDateTime?) -> Unit = { _, _ -> }, + stationSelected: (Int, ZonedDateTime?) -> Unit = { _, _ -> }, userSelected: (String) -> Unit = { }, statusSelected: (Int) -> Unit = { }, handleEditClicked: (Status) -> Unit = { }, @@ -150,7 +152,7 @@ fun CheckInCard( top.linkTo(parent.top) width = Dimension.fillToConstraints }, - stationName = status.journey.origin.name, + station = status.journey.origin, timePlanned = status.journey.origin.departurePlanned, timeReal = status.journey.departureManual ?: status.journey.origin.departureReal, stationSelected = stationSelected @@ -165,7 +167,7 @@ fun CheckInCard( bottom.linkTo(parent.bottom) width = Dimension.fillToConstraints }, - stationName = status.journey.destination.name, + station = status.journey.destination, timePlanned = status.journey.destination.arrivalPlanned, timeReal = status.journey.arrivalManual ?: status.journey.destination.arrivalReal, verticalAlignment = Alignment.Bottom, @@ -256,11 +258,11 @@ private fun calculateProgress( @Composable private fun StationRow( modifier: Modifier = Modifier, - stationName: String, + station: HafasTrainTripStation, timePlanned: ZonedDateTime, timeReal: ZonedDateTime?, verticalAlignment: Alignment.Vertical = Alignment.Top, - stationSelected: (String, ZonedDateTime?) -> Unit = { _, _ -> } + stationSelected: (Int, ZonedDateTime?) -> Unit = { _, _ -> } ) { val primaryColor = LocalColorScheme.current.primary @@ -274,8 +276,8 @@ private fun StationRow( ) { Text( modifier = Modifier - .clickable { stationSelected(stationName, null) }, - text = stationName, + .clickable { stationSelected(station.id, null) }, + text = station.name, style = AppTypography.titleLarge, overflow = TextOverflow.Ellipsis, maxLines = 2, @@ -292,7 +294,7 @@ private fun StationRow( else timePlanned Text( - modifier = Modifier.clickable { stationSelected(stationName, displayedDate) }, + modifier = Modifier.clickable { stationSelected(station.id, displayedDate) }, text = getLocalTimeString( date = displayedDate ), diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/search/Search.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/search/Search.kt index 1bb3ed93..3208bc61 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/search/Search.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/search/Search.kt @@ -63,7 +63,7 @@ fun Search( queryUsers: Boolean = true, homelandStation: Station? = null, recentStations: List? = null, - onStationSelected: (String) -> Unit = { }, + onStationSelected: (Int) -> Unit = { }, onUserSelected: (User) -> Unit = { } ) { val searchInstruction = @@ -99,7 +99,7 @@ fun Search( val stationSelected: (Station) -> Unit = { active = false - onStationSelected(it.name) + onStationSelected(it.id) } val userSelected: (User) -> Unit = { active = false @@ -158,10 +158,7 @@ fun Search( DockedSearchBar( query = query, onQueryChange = { query = it }, - onSearch = { - active = false - onStationSelected(it) - }, + onSearch = { query = it }, active = active, onActiveChange = { active = it }, modifier = modifier, diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/search/SearchViewModel.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/search/SearchViewModel.kt index d1359f03..9836a66c 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/search/SearchViewModel.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/search/SearchViewModel.kt @@ -23,7 +23,7 @@ class SearchViewModel : ViewModel() { ): List? { return try { val stations = TraewellingApi.travelService.autoCompleteStationSearch(query).data - stations.sortedWith(compareBy(nullsLast()) { it.ds100 }).take(5) + stations.sortedWith(compareBy(nullsLast()) { it.ds100 }) } catch (_: Exception) { null } diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnection.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnection.kt index 845ad999..a1039497 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnection.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnection.kt @@ -23,11 +23,15 @@ import androidx.compose.material3.rememberDatePickerState import androidx.compose.material3.rememberTimePickerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -44,6 +48,7 @@ import de.hbch.traewelling.R import de.hbch.traewelling.api.models.station.Station import de.hbch.traewelling.api.models.trip.HafasLine import de.hbch.traewelling.api.models.trip.HafasTrip +import de.hbch.traewelling.api.models.trip.HafasTripPage import de.hbch.traewelling.api.models.trip.ProductType import de.hbch.traewelling.shared.CheckInViewModel import de.hbch.traewelling.shared.LoggedInUserViewModel @@ -59,6 +64,7 @@ import de.hbch.traewelling.ui.include.cardSearchStation.CardSearch import de.hbch.traewelling.util.getDelayColor import de.hbch.traewelling.util.getLastDestination import de.hbch.traewelling.util.getLocalTimeString +import kotlinx.coroutines.launch import java.time.Instant import java.time.ZonedDateTime import java.time.ZoneId @@ -67,37 +73,32 @@ import java.time.ZoneId fun SearchConnection( loggedInUserViewModel: LoggedInUserViewModel, checkInViewModel: CheckInViewModel, - station: String, + station: Int, currentSearchDate: ZonedDateTime, onTripSelected: () -> Unit = { }, onHomelandSelected: (Station) -> Unit = { } ) { val viewModel: SearchConnectionViewModel = viewModel() - var stationName by remember { mutableStateOf(station) } + val coroutineScope = rememberCoroutineScope() + + var hafasTripPage by remember { mutableStateOf(null) } + var stationId by rememberSaveable { mutableIntStateOf(station) } + val stationName by remember { derivedStateOf { hafasTripPage?.meta?.station?.name ?: "" } } + val trips by remember { derivedStateOf { hafasTripPage?.data ?: listOf() } } + val times by remember { derivedStateOf { hafasTripPage?.meta?.times } } + val scrollState = rememberScrollState() - val trips = remember { mutableStateListOf() } - val times by viewModel.pageTimes.observeAsState() var searchDate by remember { mutableStateOf(currentSearchDate) } var loading by remember { mutableStateOf(false) } - var searchConnections by remember { mutableStateOf(true) } var selectedFilter by remember { mutableStateOf(null) } - LaunchedEffect(searchConnections, selectedFilter) { - if (searchConnections) { - loading = true - viewModel.searchConnections( - stationName, - searchDate, - selectedFilter, - { - loading = false - searchConnections = false - trips.clear() - trips.addAll(it.data) - stationName = it.meta.station.name - }, - { } - ) + LaunchedEffect(stationId, searchDate, selectedFilter) { + loading = true + + coroutineScope.launch { + val tripPage = viewModel.searchConnections(stationId, searchDate, selectedFilter) + loading = false + hafasTripPage = tripPage } } @@ -109,8 +110,7 @@ fun SearchConnection( ) { CardSearch( onStationSelected = { station -> - stationName = station - searchConnections = true + stationId = station }, homelandStationData = loggedInUserViewModel.home, recentStationsData = loggedInUserViewModel.lastVisitedStations, @@ -138,14 +138,12 @@ fun SearchConnection( val time = times?.previous time?.let { searchDate = it - searchConnections = true } }, onNextTime = { val time = times?.next time?.let { searchDate = it - searchConnections = true } }, onTripSelection = { trip -> @@ -163,22 +161,19 @@ fun SearchConnection( }, onTimeSelection = { searchDate = it - searchConnections = true }, onHomelandStationSelection = { - viewModel.setUserHomelandStation( - station, - { s -> + coroutineScope.launch { + val s = viewModel.setUserHomelandStation(stationId) + if (s != null) { loggedInUserViewModel.setHomelandStation(s) onHomelandSelected(s) - }, - {} - ) + } + } }, appliedFilter = selectedFilter, onFilter = { selectedFilter = it - searchConnections = true } ) } diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnectionViewModel.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnectionViewModel.kt index bb985c49..7645eee1 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnectionViewModel.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/searchConnection/SearchConnectionViewModel.kt @@ -1,83 +1,40 @@ package de.hbch.traewelling.ui.searchConnection -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import de.hbch.traewelling.api.TraewellingApi -import de.hbch.traewelling.api.models.Data -import de.hbch.traewelling.api.models.meta.Times import de.hbch.traewelling.api.models.station.Station import de.hbch.traewelling.api.models.trip.HafasTripPage -import de.hbch.traewelling.logging.Logger -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response import java.time.ZonedDateTime class SearchConnectionViewModel: ViewModel() { - private val _pageTimes = MutableLiveData() - val pageTimes: LiveData get() = _pageTimes - - fun searchConnections( - stationName: String, + suspend fun searchConnections( + stationId: Int, departureTime: ZonedDateTime, - filterType: FilterType?, - successCallback: (HafasTripPage) -> Unit, - failureCallback: () -> Unit - ) { - val requestStationName = stationName.replace('/', ' ') - TraewellingApi.travelService.getDeparturesAtStation( - requestStationName, - departureTime, - filterType?.filterQuery ?: "" - ).enqueue(object: Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - val trip = response.body() - if (trip != null) { - successCallback(trip) - _pageTimes.postValue(trip.meta.times) - return - } - } - failureCallback() - } - override fun onFailure(call: Call, t: Throwable) { - failureCallback() - Logger.captureException(t) - } - }) - } + filterType: FilterType? + ): HafasTripPage? { + return try { + val tripPage = TraewellingApi + .travelService + .getDeparturesAtStation( + stationId, + departureTime, + filterType?.filterQuery ?: "" + ) - fun setUserHomelandStation( - stationName: String, - successCallback: (Station) -> Unit, - failureCallback: () -> Unit - ) { - TraewellingApi.authService.setUserHomelandStation(stationName) - .enqueue(object: Callback> { - override fun onResponse( - call: Call>, - response: Response> - ) { - if (response.isSuccessful) { - val station = response.body()?.data - if (station != null) { - successCallback(station) - return - } - } - failureCallback() - } + tripPage + } catch (_: Exception) { + null + } + } - override fun onFailure(call: Call>, t: Throwable) { - failureCallback() - Logger.captureException(t) - } - }) + suspend fun setUserHomelandStation( + stationId: Int + ): Station? { + return try { + TraewellingApi.authService.setUserHomelandStation(stationId).data + } catch (_: Exception) { + null + } } } diff --git a/app/src/main/kotlin/de/hbch/traewelling/ui/user/Profile.kt b/app/src/main/kotlin/de/hbch/traewelling/ui/user/Profile.kt index 822dabb4..75bedf90 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/ui/user/Profile.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/ui/user/Profile.kt @@ -34,7 +34,7 @@ import java.time.ZonedDateTime fun Profile( username: String?, loggedInUserViewModel: LoggedInUserViewModel, - stationSelectedAction: (String, ZonedDateTime?) -> Unit = { _, _ -> }, + stationSelectedAction: (Int, ZonedDateTime?) -> Unit = { _, _ -> }, statusSelectedAction: (Int) -> Unit = { }, statusDeletedAction: () -> Unit = { }, statusEditAction: (Status) -> Unit = { }, diff --git a/app/src/main/kotlin/de/hbch/traewelling/util/Extensions.kt b/app/src/main/kotlin/de/hbch/traewelling/util/Extensions.kt index efaccff0..885b8bd9 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/util/Extensions.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/util/Extensions.kt @@ -37,6 +37,7 @@ import de.hbch.traewelling.BuildConfig import de.hbch.traewelling.R import de.hbch.traewelling.api.TraewellingApi import de.hbch.traewelling.api.models.lineIcons.LineIcon +import de.hbch.traewelling.api.models.station.Station import de.hbch.traewelling.api.models.status.Status import de.hbch.traewelling.logging.Logger import de.hbch.traewelling.shared.FeatureFlags @@ -80,7 +81,7 @@ fun LazyListScope.checkInList( checkIns: SnapshotStateList, checkInCardViewModel: CheckInCardViewModel, loggedInUserViewModel: LoggedInUserViewModel, - stationSelectedAction: (String, ZonedDateTime?) -> Unit = { _, _ -> }, + stationSelectedAction: (Int, ZonedDateTime?) -> Unit = { _, _ -> }, statusSelectedAction: (Int) -> Unit = { }, statusEditAction: (Status) -> Unit = { }, statusDeletedAction: () -> Unit = { }, diff --git a/app/src/main/kotlin/de/hbch/traewelling/util/ShortcutsUtil.kt b/app/src/main/kotlin/de/hbch/traewelling/util/ShortcutsUtil.kt index 543cff16..02dc81ab 100644 --- a/app/src/main/kotlin/de/hbch/traewelling/util/ShortcutsUtil.kt +++ b/app/src/main/kotlin/de/hbch/traewelling/util/ShortcutsUtil.kt @@ -24,7 +24,7 @@ fun Station.toShortcut(context: Context, shortcutId: String, home: Boolean = fal TraewelldroidUriBuilder() .appendPath("trains") .appendPath("stationboard") - .appendQueryParameter("station", ibnr) + .appendQueryParameter("station", id.toString()) .build() ) ) diff --git a/build.gradle b/build.gradle index b42a3b8d..3928c335 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:8.3.1' + classpath 'com.android.tools.build:gradle:8.4.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.7.7" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:11.1.3" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5446286c..ad7d08d6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Aug 08 15:43:24 CEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME