diff --git a/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/Analytics.kt b/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/Analytics.kt index a1296be9..0fcb9f68 100644 --- a/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/Analytics.kt +++ b/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/Analytics.kt @@ -4,7 +4,7 @@ import xyz.ksharma.krail.core.analytics.event.AnalyticsEvent interface Analytics { - fun track(event: AnalyticsEvent, properties: Map? = null) + fun track(event: AnalyticsEvent) fun setUserId(userId: String) diff --git a/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/RealAnalytics.kt b/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/RealAnalytics.kt index a6055ec0..76a17aaa 100644 --- a/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/RealAnalytics.kt +++ b/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/RealAnalytics.kt @@ -5,8 +5,8 @@ import xyz.ksharma.krail.core.analytics.event.AnalyticsEvent class RealAnalytics(private val firebaseAnalytics: FirebaseAnalytics) : Analytics { - override fun track(event: AnalyticsEvent, properties: Map?) { - firebaseAnalytics.logEvent(event.name, properties) + override fun track(event: AnalyticsEvent) { + firebaseAnalytics.logEvent(event.name, event.properties) } override fun setUserId(userId: String) { diff --git a/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/event/AnalyticsEvent.kt b/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/event/AnalyticsEvent.kt index 59b7b5cf..16716a2c 100644 --- a/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/event/AnalyticsEvent.kt +++ b/core/analytics/src/commonMain/kotlin/xyz/ksharma/krail/core/analytics/event/AnalyticsEvent.kt @@ -44,6 +44,12 @@ sealed class AnalyticsEvent(val name: String, val properties: Map? // endregion + // region PlanTripScreen / DateTimeSelection Screen + + data object ResetTimeClickEvent : AnalyticsEvent("reset_time_click") + + // endregion + // region TimeTable Screen data class ReverseTimeTableClickEvent(val fromStopId: String, val toStopId: String) : @@ -64,13 +70,14 @@ sealed class AnalyticsEvent(val name: String, val properties: Map? properties = mapOf("fromStopId" to fromStopId, "toStopId" to toStopId), ) - data class DateTimeSelectEvent(val dayOfWeek: String, val time: String, val type: String) : - AnalyticsEvent( - name = "date_time_select", - properties = mapOf("dayOfWeek" to dayOfWeek, "time" to time, "type" to type), - ) - - data object ResetTimeClickEvent : AnalyticsEvent("reset_time_click") + data class DateTimeSelectEvent( + val dayOfWeek: String, + val time: String, + val journeyOption: String, + ) : AnalyticsEvent( + name = "date_time_select", + properties = mapOf("dayOfWeek" to dayOfWeek, "time" to time, "option" to journeyOption), + ) data class JourneyCardExpandEvent(val hasStarted: Boolean) : AnalyticsEvent( @@ -78,9 +85,15 @@ sealed class AnalyticsEvent(val name: String, val properties: Map? properties = mapOf("hasStarted" to hasStarted), ) - data object JourneyCardCollapseEvent : AnalyticsEvent(name = "journey_card_collapse") + data class JourneyCardCollapseEvent(val hasStarted: Boolean) : AnalyticsEvent( + name = "journey_card_collapse", + properties = mapOf("hasStarted" to hasStarted), + ) - data object JourneyLegClickEvent : AnalyticsEvent(name = "journey_leg_click") + data class JourneyLegClickEvent(val expanded: Boolean) : AnalyticsEvent( + name = "journey_leg_click", + properties = mapOf("expanded" to expanded), + ) data class JourneyAlertClickEvent(val fromStopId: String, val toStopId: String) : AnalyticsEvent( diff --git a/feature/trip-planner/state/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/state/timetable/TimeTableUiEvent.kt b/feature/trip-planner/state/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/state/timetable/TimeTableUiEvent.kt index e2b8b1ea..7af2b8fa 100644 --- a/feature/trip-planner/state/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/state/timetable/TimeTableUiEvent.kt +++ b/feature/trip-planner/state/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/state/timetable/TimeTableUiEvent.kt @@ -3,12 +3,21 @@ package xyz.ksharma.krail.trip.planner.ui.state.timetable import xyz.ksharma.krail.trip.planner.ui.state.datetimeselector.DateTimeSelectionItem sealed interface TimeTableUiEvent { + data object SaveTripButtonClicked : TimeTableUiEvent + data class LoadTimeTable(val trip: Trip) : TimeTableUiEvent + data class JourneyCardClicked(val journeyId: String) : TimeTableUiEvent + data class DateTimeSelectionChanged(val dateTimeSelectionItem: DateTimeSelectionItem?) : TimeTableUiEvent data object ReverseTripButtonClicked : TimeTableUiEvent + data object RetryButtonClicked : TimeTableUiEvent + + data object AnalyticsDateTimeSelectorClicked : TimeTableUiEvent + + data class JourneyLegClicked(val expanded: Boolean) : TimeTableUiEvent } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/JourneyCard.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/JourneyCard.kt index 5476dc6b..ac5e01c5 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/JourneyCard.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/JourneyCard.kt @@ -91,6 +91,7 @@ fun JourneyCard( totalUniqueServiceAlerts: Int, modifier: Modifier = Modifier, onAlertClick: () -> Unit = {}, + onLegClick: (Boolean) -> Unit = {}, ) { val onSurface: Color = KrailTheme.colors.onSurface val borderColors = remember(transportModeList) { transportModeList.toColors(onSurface) } @@ -173,6 +174,7 @@ fun JourneyCard( legList = legList, totalUniqueServiceAlerts = totalUniqueServiceAlerts, onAlertClick = onAlertClick, + onLegClick = onLegClick, modifier = Modifier.clickable( role = Role.Button, onClick = onClick, @@ -196,6 +198,7 @@ fun ExpandedJourneyCardContent( legList: ImmutableList, totalUniqueServiceAlerts: Int, onAlertClick: () -> Unit, + onLegClick: (Boolean) -> Unit, modifier: Modifier = Modifier, ) { val appPlatformType: AppPlatformType = LocalAppPlatformProvider.current @@ -319,6 +322,7 @@ fun ExpandedJourneyCardContent( lastLeg = legList[(index - 1).coerceAtLeast(0)] ) else 0.dp ), + onClick = onLegClick, ) } @@ -535,6 +539,7 @@ private fun PreviewJourneyCard() { totalWalkTime = null, totalUniqueServiceAlerts = 0, onClick = {}, + onLegClick = {}, ) } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/LegView.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/LegView.kt index 067eaa60..ccf0c347 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/LegView.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/components/LegView.kt @@ -59,6 +59,7 @@ fun LegView( displayDuration: Boolean, modifier: Modifier = Modifier, displayAllStops: Boolean = false, + onClick: (Boolean) -> Unit = {}, ) { val circleRadius = 8.dp val strokeWidth = 4.dp @@ -83,7 +84,10 @@ fun LegView( .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null, - onClick = { showIntermediateStops = !showIntermediateStops }, + onClick = { + showIntermediateStops = !showIntermediateStops + onClick(showIntermediateStops) + }, role = Role.Button, ) .padding(vertical = 12.dp, horizontal = 12.dp), diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt index 6a6f2f03..c9bc3b3f 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsDestination.kt @@ -101,20 +101,23 @@ internal fun NavGraphBuilder.savedTripsDestination(navController: NavHostControl // Timber.e("Select both stops") } }, - onSearchButtonClick = { fromStop, toStop -> - if (fromStop != null && toStop != null) { + onSearchButtonClick = { + if (fromStopItem != null && toStopItem != null) { + val fromStopId = fromStopItem!!.stopId + val toStopId = toStopItem!!.stopId + viewModel.onEvent( SavedTripUiEvent.AnalyticsLoadTimeTableClick( - fromStopId = fromStop.stopId, - toStopId = toStop.stopId, + fromStopId = fromStopId, + toStopId = toStopId, ) ) navController.navigate( route = TimeTableRoute( - fromStopId = fromStop.stopId, - fromStopName = fromStop.stopName, - toStopId = toStop.stopId, - toStopName = toStop.stopName, + fromStopId = fromStopId, + fromStopName = fromStopItem!!.stopName, + toStopId = toStopId, + toStopName = toStopItem!!.stopName, ), navOptions = NavOptions.Builder().setLaunchSingleTop(true).build(), ) diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsScreen.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsScreen.kt index b08ec0bb..2956ef2f 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsScreen.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/savedtrips/SavedTripsScreen.kt @@ -45,7 +45,7 @@ fun SavedTripsScreen( toButtonClick: () -> Unit = {}, onReverseButtonClick: () -> Unit = {}, onSavedTripCardClick: (StopItem?, StopItem?) -> Unit = { _, _ -> }, - onSearchButtonClick: (StopItem?, StopItem?) -> Unit = { _, _ -> }, + onSearchButtonClick: () -> Unit = {}, onSettingsButtonClick: () -> Unit = {}, onEvent: (SavedTripUiEvent) -> Unit = {}, ) { @@ -126,16 +126,6 @@ fun SavedTripsScreen( stopName = trip.toStopName, ), ) - onSearchButtonClick( - StopItem( - stopId = trip.fromStopId, - stopName = trip.fromStopName, - ), - StopItem( - stopId = trip.toStopId, - stopName = trip.toStopName, - ), - ) }, primaryTransportMode = null, // TODO modifier = Modifier @@ -155,7 +145,7 @@ fun SavedTripsScreen( fromButtonClick = fromButtonClick, toButtonClick = toButtonClick, onReverseButtonClick = onReverseButtonClick, - onSearchButtonClick = { onSearchButtonClick(null, null) }, + onSearchButtonClick = { onSearchButtonClick() }, ) } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableAnalytics.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableAnalytics.kt new file mode 100644 index 00000000..f451f93a --- /dev/null +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableAnalytics.kt @@ -0,0 +1,12 @@ +package xyz.ksharma.krail.trip.planner.ui.timetable + +import xyz.ksharma.krail.core.analytics.Analytics +import xyz.ksharma.krail.core.analytics.event.AnalyticsEvent + +internal fun Analytics.trackJourneyCardExpandEvent(hasStarted: Boolean) { + track(AnalyticsEvent.JourneyCardExpandEvent(hasStarted = hasStarted)) +} + +internal fun Analytics.trackJourneyCardCollapseEvent(hasStarted: Boolean) { + track(AnalyticsEvent.JourneyCardCollapseEvent(hasStarted = hasStarted)) +} diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableDestination.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableDestination.kt index 5572e705..f6377655 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableDestination.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableDestination.kt @@ -70,11 +70,15 @@ internal fun NavGraphBuilder.timeTableDestination(navController: NavHostControll }, dateTimeSelectionItem = dateTimeSelectionItem, dateTimeSelectorClicked = { + viewModel.onEvent(TimeTableUiEvent.AnalyticsDateTimeSelectorClicked) navController.navigate( route = DateTimeSelectorRoute(dateTimeSelectionItem?.toJsonString()), navOptions = NavOptions.Builder().setLaunchSingleTop(singleTop = true).build(), ) }, + onJourneyLegClick = { journeyId -> + viewModel.onEvent(TimeTableUiEvent.JourneyLegClicked(journeyId)) + }, ) } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt index a4f6196d..178934b2 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableScreen.kt @@ -3,8 +3,6 @@ package xyz.ksharma.krail.trip.planner.ui.timetable import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.animation.scaleIn -import androidx.compose.animation.scaleOut import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -75,6 +73,7 @@ fun TimeTableScreen( onEvent: (TimeTableUiEvent) -> Unit, onAlertClick: (String) -> Unit, onBackClick: () -> Unit, + onJourneyLegClick: (Boolean) -> Unit, modifier: Modifier = Modifier, dateTimeSelectorClicked: () -> Unit = {}, ) { @@ -243,6 +242,7 @@ fun TimeTableScreen( onAlertClick = { onAlertClick(journey.journeyId) }, + onLegClick = onJourneyLegClick, modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp) .animateItem(), ) @@ -282,6 +282,7 @@ private fun JourneyCardItem( totalUniqueServiceAlerts: Int, modifier: Modifier = Modifier, transportModeLineList: ImmutableList? = null, + onLegClick: (Boolean) -> Unit, ) { if (!transportModeLineList.isNullOrEmpty() && legList.isNotEmpty()) { JourneyCard( @@ -300,6 +301,7 @@ private fun JourneyCardItem( onClick = onClick, onAlertClick = onAlertClick, totalUniqueServiceAlerts = totalUniqueServiceAlerts, + onLegClick = onLegClick, modifier = modifier, ) } @@ -364,6 +366,7 @@ private fun PreviewTimeTableScreen() { onAlertClick = {}, onBackClick = {}, dateTimeSelectionItem = null, + onJourneyLegClick = {}, ) } } @@ -390,6 +393,7 @@ private fun PreviewTimeTableScreenError() { onAlertClick = {}, onBackClick = {}, dateTimeSelectionItem = null, + onJourneyLegClick = {}, ) } } @@ -416,6 +420,7 @@ private fun PreviewTimeTableScreenNoResults() { onEvent = {}, onAlertClick = {}, onBackClick = {}, + onJourneyLegClick = {}, ) } } diff --git a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableViewModel.kt b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableViewModel.kt index f6934645..db78b2db 100644 --- a/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableViewModel.kt +++ b/feature/trip-planner/ui/src/commonMain/kotlin/xyz/ksharma/krail/trip/planner/ui/timetable/TimeTableViewModel.kt @@ -21,6 +21,7 @@ import kotlinx.datetime.Clock import kotlinx.datetime.Instant import xyz.ksharma.krail.core.analytics.Analytics import xyz.ksharma.krail.core.analytics.AnalyticsScreen +import xyz.ksharma.krail.core.analytics.event.AnalyticsEvent import xyz.ksharma.krail.core.analytics.event.trackScreenViewEvent import xyz.ksharma.krail.core.datetime.DateTimeHelper.calculateTimeDifferenceFromNow import xyz.ksharma.krail.core.datetime.DateTimeHelper.isBefore @@ -115,13 +116,31 @@ class TimeTableViewModel( fun onEvent(event: TimeTableUiEvent) { when (event) { is TimeTableUiEvent.LoadTimeTable -> onLoadTimeTable(event.trip) + is TimeTableUiEvent.JourneyCardClicked -> onJourneyCardClicked(event.journeyId) + TimeTableUiEvent.SaveTripButtonClicked -> onSaveTripButtonClicked() + TimeTableUiEvent.ReverseTripButtonClicked -> onReverseTripButtonClicked() + TimeTableUiEvent.RetryButtonClicked -> onLoadTimeTable(tripInfo!!) + is TimeTableUiEvent.DateTimeSelectionChanged -> { onDateTimeSelectionChanged(item = event.dateTimeSelectionItem) } + + TimeTableUiEvent.AnalyticsDateTimeSelectorClicked -> { + analytics.track( + AnalyticsEvent.PlanTripClickEvent( + fromStopId = tripInfo?.fromStopId ?: "NA", + toStopId = tripInfo?.toStopId ?: "NA", + ) + ) + } + + is TimeTableUiEvent.JourneyLegClicked -> { + analytics.track(AnalyticsEvent.JourneyLegClickEvent(expanded = event.expanded)) + } } } @@ -134,6 +153,14 @@ class TimeTableViewModel( journeys.clear() // Clear cache trips when date time selection changed. alertsCache.clearAlerts() rateLimiter.triggerEvent() + + analytics.track( + AnalyticsEvent.DateTimeSelectEvent( + dayOfWeek = item?.date?.dayOfWeek?.name ?: "NA", + time = item?.toHHMM() ?: "NA", + journeyOption = item?.option?.name ?: "NA", + ) + ) } } @@ -245,6 +272,13 @@ class TimeTableViewModel( private fun onSaveTripButtonClicked() { println("Save Trip Button Clicked") viewModelScope.launch(Dispatchers.IO) { + analytics.track( + AnalyticsEvent.SaveTripClickEvent( + fromStopId = tripInfo?.fromStopId ?: "NA", + toStopId = tripInfo?.toStopId ?: "NA", + ), + ) + tripInfo?.let { trip -> println("Toggle Save Trip: $trip") val savedTrip = sandook.selectTripById(tripId = trip.tripId) @@ -269,7 +303,14 @@ class TimeTableViewModel( private fun onJourneyCardClicked(journeyId: String) { println("Journey Card Clicked(JourneyId): $journeyId") + val hasJourneyStarted = journeys[journeyId]?.hasJourneyStarted ?: false + val expandedJourneyId = _expandedJourneyId.value _expandedJourneyId.update { if (it == journeyId) null else journeyId } + if (expandedJourneyId == journeyId) { + analytics.trackJourneyCardCollapseEvent(hasStarted = hasJourneyStarted) + } else { + analytics.trackJourneyCardExpandEvent(hasStarted = hasJourneyStarted) + } } private fun onLoadTimeTable(trip: Trip) { @@ -301,6 +342,13 @@ class TimeTableViewModel( journeys.clear() // Clear cache trips when reverse trip is clicked. alertsCache.clearAlerts() // Clear alerts cache when reverse trip is clicked. + analytics.track( + AnalyticsEvent.ReverseTimeTableClickEvent( + fromStopId = tripInfo!!.fromStopId, + toStopId = tripInfo!!.toStopId, + ) + ) + val savedTrip = sandook.selectTripById(tripId = reverseTrip.tripId) updateUiState { copy( @@ -354,6 +402,14 @@ class TimeTableViewModel( }.orEmpty() }.getOrElse { emptyList() } } + if (alerts.isNotEmpty()) { + analytics.track( + AnalyticsEvent.JourneyAlertClickEvent( + fromStopId = tripInfo?.fromStopId ?: "NA", + toStopId = tripInfo?.toStopId ?: "NA", + ), + ) + } onResult(alerts) } }