From e763f3af70e15a7fb74b0466142a1565298c073d Mon Sep 17 00:00:00 2001 From: Karan Sharma <55722391+ksharma-xyz@users.noreply.github.com> Date: Fri, 14 Feb 2025 07:57:24 +1100 Subject: [PATCH] UI: Add mode selection analytics tracking (#614) ### TL;DR Added analytics tracking for mode selection events in trip planner ### What changed? - Added new analytics events: `ModeClickEvent` and `ModeSelectionDoneEvent` - Integrated mode selection tracking in TimeTableViewModel - Added new UI event `ModeClicked` to handle mode selection visibility - Connected mode click analytics to the UI through TimeTableScreen and TimeTableDestination ### How to test? 1. Open trip planner 2. Click on mode selection button 3. Verify analytics event is fired with correct fromStopId, toStopId, and displayModeSelectionRow state 4. Select/deselect different transport modes 5. Verify mode selection analytics event fires with correct unselected modes ### Why make this change? To track user interaction with transport mode selection, providing insights into how users filter their journey planning options and which transport modes are commonly excluded from searches. --- .../core/analytics/event/AnalyticsEvent.kt | 26 +++++++++++++++++++ .../ui/state/timetable/TimeTableUiEvent.kt | 5 ++++ .../ui/timetable/TimeTableDestination.kt | 3 +++ .../planner/ui/timetable/TimeTableScreen.kt | 2 ++ .../ui/timetable/TimeTableViewModel.kt | 22 +++++++++++++++- 5 files changed, 57 insertions(+), 1 deletion(-) 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 bce9a423..8dff3576 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 @@ -84,6 +84,32 @@ sealed class AnalyticsEvent(val name: String, val properties: Map? properties = mapOf("fromStopId" to fromStopId, "toStopId" to toStopId), ) + data class ModeClickEvent( + val fromStopId: String, + val toStopId: String, + val displayModeSelectionRow: Boolean, + ) : + AnalyticsEvent( + name = "mode_click", + properties = mapOf( + "fromStopId" to fromStopId, + "toStopId" to toStopId, + "displayModeSelectionRow" to displayModeSelectionRow, + ), + ) + + data class ModeSelectionDoneEvent( + val fromStopId: String, val toStopId: String, + val unselectedProductClasses: Set, + ) : AnalyticsEvent( + name = "mode_selection_done", + properties = mapOf( + "fromStopId" to fromStopId, + "toStopId" to toStopId, + "unselected" to unselectedProductClasses.toString(), + ), + ) + data class DateTimeSelectEvent( val dayOfWeek: String, val time: String, 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 f943887b..684c6539 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 @@ -22,4 +22,9 @@ sealed interface TimeTableUiEvent { data class JourneyLegClicked(val expanded: Boolean) : TimeTableUiEvent data class ModeSelectionChanged(val unselectedModes: Set) : TimeTableUiEvent + + /** + * when tru, the selection row is displayed else it is hidden. + */ + data class ModeClicked(val displayModeSelectionRow: Boolean) : TimeTableUiEvent } 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 e7d5f1d6..c355c9c0 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 @@ -83,6 +83,9 @@ internal fun NavGraphBuilder.timeTableDestination(navController: NavHostControll onModeSelectionChanged = { unselectedModes -> log("onModeSelectionChanged Exclude :$unselectedModes") viewModel.onEvent(TimeTableUiEvent.ModeSelectionChanged(unselectedModes)) + }, + onModeClick = { displayModeSelectionRow -> + viewModel.onEvent(TimeTableUiEvent.ModeClicked(displayModeSelectionRow)) } ) } 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 b5c63d5d..47a4258f 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 @@ -85,6 +85,7 @@ fun TimeTableScreen( modifier: Modifier = Modifier, dateTimeSelectorClicked: () -> Unit = {}, onModeSelectionChanged: (Set) -> Unit = {}, + onModeClick: (Boolean) -> Unit = {}, ) { val themeColorHex by LocalThemeColor.current val themeColor = themeColorHex.hexToComposeColor() @@ -189,6 +190,7 @@ fun TimeTableScreen( SubtleButton( onClick = { displayModeSelectionRow = !displayModeSelectionRow + onModeClick(displayModeSelectionRow) }, dimensions = ButtonDefaults.mediumButtonSize(), ) { 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 5ed9b869..d42d1764 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 @@ -5,7 +5,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -152,9 +151,21 @@ class TimeTableViewModel( } is TimeTableUiEvent.ModeSelectionChanged -> onModeSelectionChanged(event.unselectedModes) + + is TimeTableUiEvent.ModeClicked -> trackOnModeClickEvent(event.displayModeSelectionRow) } } + private fun trackOnModeClickEvent(displayModeSelectionRow: Boolean) { + analytics.track( + AnalyticsEvent.ModeClickEvent( + fromStopId = tripInfo?.fromStopId ?: "NA", + toStopId = tripInfo?.toStopId ?: "NA", + displayModeSelectionRow = displayModeSelectionRow, + ) + ) + } + private fun onModeSelectionChanged(unselectedModes: Set) { if (hasModeSelectionChanged(unselectedModes)) { this.unselectedModes.clear() @@ -163,6 +174,15 @@ class TimeTableViewModel( // call api rateLimiter.triggerEvent() updateUiState { copy(isLoading = true) } + + // analytics + analytics.track( + AnalyticsEvent.ModeSelectionDoneEvent( + fromStopId = tripInfo?.fromStopId ?: "NA", + toStopId = tripInfo?.toStopId ?: "NA", + unselectedProductClasses = this.unselectedModes, + ) + ) } else { // do nothing. log("Mode selection not changed")