diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddContract.kt index 598e2072..7282898f 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddContract.kt @@ -7,6 +7,8 @@ sealed interface EnvelopeAddEffect : SideEffect { data object PopBackStack : EnvelopeAddEffect data object PopBackStackWithRefresh : EnvelopeAddEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : EnvelopeAddEffect + data class LogClickNextButton(val step: EnvelopeAddStep) : EnvelopeAddEffect + data class LogClickBackButton(val step: EnvelopeAddStep) : EnvelopeAddEffect } /** diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddViewModel.kt index ee0d323b..84d0f136 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeadd/EnvelopeAddViewModel.kt @@ -209,4 +209,12 @@ class EnvelopeAddViewModel @Inject constructor( buttonEnabled = date != null, ) } + + fun logBackButtonClickEvent() { + postSideEffect(EnvelopeAddEffect.LogClickBackButton(currentState.currentStep)) + } + + fun logNextButtonClickEvent() { + postSideEffect(EnvelopeAddEffect.LogClickNextButton(currentState.currentStep)) + } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeadd/SentEnvelopeAddScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeadd/SentEnvelopeAddScreen.kt index 0739bdcf..ca5fd97c 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeadd/SentEnvelopeAddScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeadd/SentEnvelopeAddScreen.kt @@ -12,13 +12,17 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.core.os.bundleOf import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.firebase.analytics.FirebaseAnalytics import com.susu.core.designsystem.component.appbar.SusuProgressAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.button.FilledButtonColor @@ -40,6 +44,7 @@ import com.susu.feature.envelopeadd.content.phone.PhoneContentRoute import com.susu.feature.envelopeadd.content.present.PresentContentRoute import com.susu.feature.envelopeadd.content.relationship.RelationshipContentRoute import com.susu.feature.envelopeadd.content.visited.VisitedContentRoute +import kotlinx.coroutines.launch import java.time.LocalDateTime @Composable @@ -51,12 +56,31 @@ fun SentEnvelopeAddRoute( handleException: (Throwable, () -> Unit) -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val context = LocalContext.current + val scope = rememberCoroutineScope() viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is EnvelopeAddEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) EnvelopeAddEffect.PopBackStack -> popBackStack() EnvelopeAddEffect.PopBackStackWithRefresh -> popBackStackWithRefresh() + is EnvelopeAddEffect.LogClickBackButton -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_envelope_add_screen_back_at_${sideEffect.step}", + ), + ) + } + + is EnvelopeAddEffect.LogClickNextButton -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_envelope_add_screen_next_at_${sideEffect.step}", + ), + ) + } } } @@ -75,8 +99,14 @@ fun SentEnvelopeAddRoute( SentEnvelopeAddScreen( uiState = uiState, categoryName = categoryName, - onClickBack = viewModel::goPrevStep, - onClickNext = viewModel::goNextStep, + onClickBack = { + viewModel.logBackButtonClickEvent() + viewModel.goPrevStep() + }, + onClickNext = { + viewModel.logNextButtonClickEvent() + viewModel.goNextStep() + }, updateParentMoney = viewModel::updateMoney, updateParentName = viewModel::updateName, updateParentFriendId = viewModel::updateFriendId, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt index 1b396575..35f10554 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt @@ -24,4 +24,8 @@ sealed interface EnvelopeFilterSideEffect : SideEffect { data object PopBackStack : EnvelopeFilterSideEffect data class PopBackStackWithFilter(val filter: String) : EnvelopeFilterSideEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : EnvelopeFilterSideEffect + + data object LogClickFriendButtonEvent : EnvelopeFilterSideEffect + data object LogClickSliderEvent : EnvelopeFilterSideEffect + data object LogClickApplyButtonEvent : EnvelopeFilterSideEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt index 3671c7d5..e5392bc5 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt @@ -15,12 +15,16 @@ import androidx.compose.foundation.layout.size import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.core.os.bundleOf import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.firebase.analytics.FirebaseAnalytics import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.button.FilledButtonColor @@ -40,6 +44,7 @@ import com.susu.feature.envelopefilter.component.SearchBar import com.susu.feature.sent.R import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.launch @OptIn(FlowPreview::class) @Composable @@ -50,11 +55,40 @@ fun EnvelopeFilterRoute( handleException: (Throwable, () -> Unit) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val context = LocalContext.current + val scope = rememberCoroutineScope() + viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is EnvelopeFilterSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) EnvelopeFilterSideEffect.PopBackStack -> popBackStack() is EnvelopeFilterSideEffect.PopBackStackWithFilter -> popBackStackWithFilter(sideEffect.filter) + EnvelopeFilterSideEffect.LogClickApplyButtonEvent -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_filter_apply_button", + ), + ) + } + + EnvelopeFilterSideEffect.LogClickFriendButtonEvent -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_filter_friend_button", + ), + ) + } + + EnvelopeFilterSideEffect.LogClickSliderEvent -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_filter_slider", + ), + ) + } } } @@ -68,15 +102,35 @@ fun EnvelopeFilterRoute( .collect(viewModel::getFriendList) } + LaunchedEffect(key1 = uiState.fromAmount, key2 = uiState.toAmount) { + snapshotFlow { uiState.fromAmount } + .debounce(1000L) + .collect { + viewModel.logSliderClickEvent() + } + + snapshotFlow { uiState.toAmount } + .debounce(1000L) + .collect { + viewModel.logSliderClickEvent() + } + } + EnvelopeFilterScreen( uiState = uiState, onClickBackIcon = viewModel::popBackStack, - onClickApplyFilterButton = viewModel::popBackStackWithFilter, + onClickApplyFilterButton = { + viewModel.popBackStackWithFilter() + viewModel.logApplyClickEvent() + }, + onClickRefreshButton = viewModel::clearFilter, onTextChangeSearch = viewModel::updateName, - onClickFriendChip = viewModel::toggleFriend, + onClickFriendChip = { friend -> + viewModel.toggleFriend(friend) + viewModel.logFriendClickEvent() + }, onCloseFriendChip = viewModel::unselectFriend, onMoneyValueChange = viewModel::updateMoneyRange, - onClickRefreshButton = viewModel::clearFilter, ) } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt index a5677c4c..d3f70077 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt @@ -126,4 +126,8 @@ class EnvelopeFilterViewModel @Inject constructor( toAmount = null, ) } + + fun logApplyClickEvent() = postSideEffect(EnvelopeFilterSideEffect.LogClickApplyButtonEvent) + fun logFriendClickEvent() = postSideEffect(EnvelopeFilterSideEffect.LogClickFriendButtonEvent) + fun logSliderClickEvent() = postSideEffect(EnvelopeFilterSideEffect.LogClickSliderEvent) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt index 96d07278..81c749fa 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt @@ -70,4 +70,8 @@ sealed interface SentEffect : SideEffect { data object ScrollToTop : SentEffect data class FocusToLastEnvelope(val lastIndex: Int) : SentEffect data object LogSearchIconClickEvent : SentEffect + data object LogFilterButtonClickEvent : SentEffect + data object LogAlignButtonClickEvent : SentEffect + data class LogAlignItemClickEvent(val align: EnvelopeAlign) : SentEffect + data object LogShowHistoryButtonClickEvent : SentEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt index 497a1406..11863bfe 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt @@ -122,6 +122,42 @@ fun SentRoute( ), ) } + + SentEffect.LogAlignButtonClickEvent -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_screen_align_button", + ), + ) + } + + SentEffect.LogFilterButtonClickEvent -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_screen_filter_button", + ), + ) + } + + SentEffect.LogShowHistoryButtonClickEvent -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_screen_show_history_button", + ), + ) + } + + is SentEffect.LogAlignItemClickEvent -> scope.launch { + FirebaseAnalytics.getInstance(context).logEvent( + FirebaseAnalytics.Event.SELECT_CONTENT, + bundleOf( + FirebaseAnalytics.Param.CONTENT_TYPE to "sent_screen_align_item_${sideEffect.align}", + ), + ) + } } } @@ -153,7 +189,10 @@ fun SentRoute( envelopesListState = envelopesListState, refreshState = refreshState, padding = padding, - onClickHistoryShowAll = viewModel::navigateSentEnvelope, + onClickHistoryShowAll = { id -> + viewModel.navigateSentEnvelope(id) + viewModel.logShowHistoryButtonClickEvent() + }, onClickAddEnvelope = viewModel::navigateSentAdd, onClickSearchIcon = { viewModel.navigateSentEnvelopeSearch() @@ -166,11 +205,20 @@ fun SentRoute( viewModel.getEnvelopesHistoryList(friendId) } }, - onClickFilterButton = viewModel::navigateEnvelopeFilter, + onClickFilterButton = { + viewModel.navigateEnvelopeFilter() + viewModel.logFilterButtonClickEvent() + }, onClickFriendClose = viewModel::unselectFriend, onClickMoneyClose = viewModel::removeMoney, - onClickAlignButton = viewModel::showAlignBottomSheet, - onClickAlignBottomSheetItem = viewModel::updateAlignPosition, + onClickAlignButton = { + viewModel.showAlignBottomSheet() + viewModel.logAlignButtonClickEvent() + }, + onClickAlignBottomSheetItem = { + viewModel.updateAlignPosition(it) + viewModel.logAlignItemClickEvent(it) + }, onDismissAlignBottomSheet = viewModel::hideAlignBottomSheet, ) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt index 958e2dd4..ad5adff3 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -29,6 +29,10 @@ class SentViewModel @Inject constructor( private var filterUri: String? = null fun logSearchIconClickEvent() = postSideEffect(SentEffect.LogSearchIconClickEvent) + fun logFilterButtonClickEvent() = postSideEffect(SentEffect.LogFilterButtonClickEvent) + fun logAlignButtonClickEvent() = postSideEffect(SentEffect.LogAlignButtonClickEvent) + fun logAlignItemClickEvent(index: Int) = postSideEffect(SentEffect.LogAlignItemClickEvent(EnvelopeAlign.entries[index])) + fun logShowHistoryButtonClickEvent() = postSideEffect(SentEffect.LogShowHistoryButtonClickEvent) fun getEnvelopesList(refresh: Boolean?, onFinish: () -> Unit = {}) = viewModelScope.launch { mutex.withLock {