diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt index f77f207cd..f5c07b4c2 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt +++ b/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -19,6 +20,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.navigation.NavController import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.currentBackStackEntryAsState import com.ramcosta.composedestinations.DestinationsNavHost @@ -36,6 +38,8 @@ import com.ramcosta.composedestinations.navargs.primitives.longNavType import com.ramcosta.composedestinations.rememberNavHostEngine import com.ramcosta.composedestinations.scope.resultRecipient import com.ramcosta.composedestinations.utils.startDestination +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.NetworkStatus import dev.arkbuilders.rate.core.presentation.ui.ConnectivityOfflineSnackbar import dev.arkbuilders.rate.core.presentation.ui.ConnectivityOfflineSnackbarVisuals import dev.arkbuilders.rate.core.presentation.ui.ConnectivityOnlineSnackbar @@ -70,31 +74,19 @@ fun MainScreen() { val engine = rememberNavHostEngine() val navController = engine.rememberNavController() val snackState = remember { SnackbarHostState() } - val ctx = LocalContext.current + val coreComponent = App.instance.coreComponent - LaunchedEffect(key1 = Unit) { - val activity = ctx.findActivity() - val intent = activity?.intent - val createNewCalc = intent?.getStringExtra(ADD_NEW_CALCULATION) ?: "" - if (createNewCalc.isNotEmpty()) { - val groupId = intent?.getLongExtra(ADD_NEW_CALCULATION_GROUP_KEY, 0L) - navController.navigate(AddQuickScreenDestination(groupId = groupId)) - intent?.removeExtra(ADD_NEW_CALCULATION_GROUP_KEY) - intent?.removeExtra(ADD_NEW_CALCULATION) - } - } - LaunchedEffect(key1 = Unit) { - App.instance.coreComponent.networkStatus().onlineStatus - .drop(1) - .collect { online -> - val visuals = - if (online) - ConnectivityOnlineSnackbarVisuals - else - ConnectivityOfflineSnackbarVisuals - snackState.showSnackbar(visuals) - } - } + HandleAddQuickCalculationIntentEffect(navController) + + ObserveNetworkStatusEffect( + networkStatus = coreComponent.networkStatus(), + snackState = snackState, + ) + + ObserveNavigationAnalyticsEffect( + navController = navController, + analyticsManager = coreComponent.analyticsManager(), + ) val isKeyboardOpen by keyboardAsState() val bottomBarVisible = rememberSaveable { mutableStateOf(false) } @@ -205,3 +197,69 @@ fun MainScreen() { } } } + +@Composable +private fun HandleAddQuickCalculationIntentEffect(navController: NavController) { + val ctx = LocalContext.current + LaunchedEffect(key1 = Unit) { + val activity = ctx.findActivity() + val intent = activity?.intent + val createNewCalc = intent?.getStringExtra(ADD_NEW_CALCULATION) ?: "" + if (createNewCalc.isNotEmpty()) { + val groupId = intent?.getLongExtra(ADD_NEW_CALCULATION_GROUP_KEY, 0L) + navController.navigate(AddQuickScreenDestination(groupId = groupId)) + intent?.removeExtra(ADD_NEW_CALCULATION_GROUP_KEY) + intent?.removeExtra(ADD_NEW_CALCULATION) + } + } +} + +@Composable +private fun ObserveNetworkStatusEffect( + networkStatus: NetworkStatus, + snackState: SnackbarHostState, +) { + LaunchedEffect(key1 = Unit) { + networkStatus.onlineStatus + .drop(1) + .collect { online -> + val visuals = + if (online) + ConnectivityOnlineSnackbarVisuals + else + ConnectivityOfflineSnackbarVisuals + snackState.showSnackbar(visuals) + } + } +} + +@Composable +private fun ObserveNavigationAnalyticsEffect( + navController: NavController, + analyticsManager: AnalyticsManager, +) { + DisposableEffect(navController) { + val listener = + NavController.OnDestinationChangedListener { _, destination, _ -> + destination.route?.let { route -> + val name = extractScreenName(route) + analyticsManager.trackScreen(name) + } + } + navController.addOnDestinationChangedListener(listener) + onDispose { + navController.removeOnDestinationChangedListener(listener) + } + } +} + +private fun extractScreenName(route: String): String { + val routeWithoutArguments = route.substringBefore("?") + + val name = + routeWithoutArguments.split("/").findLast { + it.contains("{").not() + } + + return name ?: route +} diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt index aa2bdd2df..cb8fd2a8d 100644 --- a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt @@ -72,8 +72,6 @@ class AddPairAlertViewModel( container(AddPairAlertScreenState()) init { - analyticsManager.trackScreen("AddPairAlertScreen") - pairAlertId?.let { setupFromExisting() checkAboveNotBelow() diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertViewModel.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertViewModel.kt index 8bb8076c7..f7dfb9e76 100644 --- a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertViewModel.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertViewModel.kt @@ -69,8 +69,6 @@ class PairAlertViewModel( ) init { - analyticsManager.trackScreen("PairAlertScreen") - intent { if (pairAlertRepo.getAll().isNotEmpty() && notificationPermissionHelper.isGranted().not() diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt index 049bebf48..c259d0737 100644 --- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt @@ -1,5 +1,6 @@ package dev.arkbuilders.rate.feature.portfolio.presentation.add +import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -84,6 +85,10 @@ fun AddAssetScreen( } } + BackHandler { + viewModel.onBackClick() + } + val state by viewModel.collectAsState() viewModel.collectSideEffect { effect -> @@ -94,7 +99,7 @@ fun AddAssetScreen( topBar = { AppTopBarBack( title = stringResource(CoreRString.portfolio_add_new_assets), - onBackClick = { navigator.popBackStack() }, + onBackClick = { viewModel.onBackClick() }, ) }, ) { diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt index 3ccfe5968..8f26c8f9c 100644 --- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt @@ -40,6 +40,8 @@ sealed class AddAssetSideEffect { data class NavigateSearchSet(val index: Int, val prohibitedCodes: List) : AddAssetSideEffect() + + data object NavigateBack : AddAssetSideEffect() } enum class SearchNavResultType { @@ -61,8 +63,6 @@ class AddAssetViewModel( container(AddAssetState()) init { - analyticsManager.trackScreen("AddAssetScreen") - intent { val group = getGroupByIdOrCreateDefaultUseCase(groupId, GroupFeatureType.Portfolio) val groups = groupRepo.getAllSorted(GroupFeatureType.Portfolio) @@ -108,6 +108,7 @@ class AddAssetViewModel( fun onAssetRemove(removeIndex: Int) = intent { + analyticsManager.logEvent("add_asset_currency_removed") reduce { state.copy( currencies = @@ -119,6 +120,7 @@ class AddAssetViewModel( fun onGroupCreate(name: String) = intent { + analyticsManager.logEvent("add_asset_group_created") val group = Group.empty(name = name) val inAvailable = state.availableGroups.any { it.name == group.name } reduce { @@ -137,6 +139,7 @@ class AddAssetViewModel( fun onGroupSelect(group: Group) = intent { + analyticsManager.logEvent("add_asset_group_selected") reduce { state.copy(group = group) } } @@ -163,6 +166,7 @@ class AddAssetViewModel( fun onAddAsset() = intent { + analyticsManager.logEvent("add_asset_added") val group = groupRepo.getByNameOrCreateNew(state.group.name, GroupFeatureType.Portfolio) val currencies = @@ -186,6 +190,7 @@ class AddAssetViewModel( fun onSetCode(index: Int) = intent { + analyticsManager.logEvent("add_asset_set_code_clicked") val prohibitedCodes = state.currencies.map { it.code }.toMutableList() prohibitedCodes.removeAt(index) postSideEffect(AddAssetSideEffect.NavigateSearchSet(index, prohibitedCodes)) @@ -193,9 +198,16 @@ class AddAssetViewModel( fun onAddCode() = intent { + analyticsManager.logEvent("add_asset_add_code_clicked") val prohibitedCodes = state.currencies.map { it.code } postSideEffect(AddAssetSideEffect.NavigateSearchAdd(prohibitedCodes)) } + + fun onBackClick() = + intent { + analyticsManager.logEvent("add_asset_back_clicked") + postSideEffect(AddAssetSideEffect.NavigateBack) + } } class AddAssetViewModelFactory @AssistedInject constructor( diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/HandleAddAssetSideEffect.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/HandleAddAssetSideEffect.kt index c61282c11..94a610ccd 100644 --- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/HandleAddAssetSideEffect.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/HandleAddAssetSideEffect.kt @@ -30,5 +30,7 @@ suspend fun handleAddAssetSideEffect( prohibitedCodes = effect.prohibitedCodes.toTypedArray(), ), ) + + AddAssetSideEffect.NavigateBack -> navigator.popBackStack() } } diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt index 505a0db85..6f8ce6040 100644 --- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt @@ -2,6 +2,7 @@ package dev.arkbuilders.rate.feature.portfolio.presentation.edit +import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -43,6 +44,7 @@ import dev.arkbuilders.rate.core.presentation.ui.InfoDialog import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen import dev.arkbuilders.rate.feature.portfolio.di.PortfolioComponentHolder import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect @Destination @Composable @@ -63,11 +65,21 @@ fun EditAssetScreen( val state by viewModel.collectAsState() + viewModel.collectSideEffect { effect -> + when (effect) { + EditAssetScreenEffect.NavigateBack -> navigator.popBackStack() + } + } + + BackHandler { + viewModel.onBackClick() + } + Scaffold( topBar = { AppTopBarBack( title = stringResource(CoreRString.asset_detail), - onBackClick = { navigator.popBackStack() }, + onBackClick = { viewModel.onBackClick() }, ) }, ) { diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetViewModel.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetViewModel.kt index 926eb8abd..535492726 100644 --- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetViewModel.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetViewModel.kt @@ -29,7 +29,9 @@ data class EditAssetScreenState( val initialized: Boolean = false, ) -sealed class EditAssetScreenEffect +sealed class EditAssetScreenEffect { + data object NavigateBack : EditAssetScreenEffect() +} private val PERSIST_AMOUNT_DEBOUNCE = 300L @@ -46,8 +48,6 @@ class EditAssetViewModel( private val inputFlow = MutableSharedFlow() init { - analyticsManager.trackScreen("EditAssetScreen") - intent { val asset = assetsRepo.getById(assetId) val name = currencyRepo.infoByCode(asset!!.code) @@ -70,6 +70,12 @@ class EditAssetViewModel( state.copy(value = validated) } } + + fun onBackClick() = + intent { + analyticsManager.logEvent("edit_asset_back_clicked") + postSideEffect(EditAssetScreenEffect.NavigateBack) + } } class EditAssetViewModelFactory @AssistedInject constructor( diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioViewModel.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioViewModel.kt index 34964f321..d654c2c3c 100644 --- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioViewModel.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioViewModel.kt @@ -75,8 +75,6 @@ class PortfolioViewModel( container(PortfolioScreenState()) init { - analyticsManager.trackScreen("PortfolioScreen") - init() } @@ -104,12 +102,14 @@ class PortfolioViewModel( fun onChangeBaseCurrency(baseCode: CurrencyCode) = intent { + analyticsManager.logEvent("portfolio_base_currency_changed") prefs.set(PreferenceKey.BaseCurrencyCode, baseCode) initPages() } fun onAssetRemove(asset: Asset) = intent { + analyticsManager.logEvent("portfolio_asset_removed") val deleted = assetsRepo.removeAsset(asset.id) if (deleted) { postSideEffect(PortfolioScreenEffect.ShowRemovedSnackbar(asset)) @@ -118,6 +118,7 @@ class PortfolioViewModel( fun undoDelete(asset: Asset) = intent { + analyticsManager.logEvent("portfolio_asset_remove_undone") assetsRepo.setAsset(asset) } @@ -129,10 +130,12 @@ class PortfolioViewModel( fun onBackClick() = intent { if (state.filter.isNotEmpty()) { + analyticsManager.logEvent("portfolio_filter_cleared_via_back") reduce { state.copy(filter = "") } } else { + analyticsManager.logEvent("portfolio_back_clicked") postSideEffect(PortfolioScreenEffect.NavigateBack) } } @@ -179,6 +182,7 @@ class PortfolioViewModel( fun onShowGroupsReorder() = intent { + analyticsManager.logEvent("portfolio_group_reorder_opened") val groups = groupRepo.getAllSorted(GroupFeatureType.Portfolio) reduce { state.copy( @@ -191,6 +195,7 @@ class PortfolioViewModel( from: Int, to: Int, ) = intent { + analyticsManager.logEvent("portfolio_group_reordered") val newGroups = groupReorderSwapUseCase( state.editGroupReorderSheetState!!.groups, @@ -211,16 +216,19 @@ class PortfolioViewModel( fun onDismissGroupsReorder() = intent { + analyticsManager.logEvent("portfolio_group_reorder_closed") reduce { state.copy(editGroupReorderSheetState = null) } } fun onShowGroupOptions(group: Group) = intent { + analyticsManager.logEvent("portfolio_group_options_opened") reduce { state.copy(editGroupOptionsSheetState = EditGroupOptionsSheetState(group)) } } fun onGroupDelete(group: Group) = intent { + analyticsManager.logEvent("portfolio_group_deleted") groupRepo.delete(group.id) val groups = groupRepo.getAllSorted(GroupFeatureType.Portfolio) reduce { @@ -237,11 +245,13 @@ class PortfolioViewModel( fun onDismissGroupOptions() = intent { + analyticsManager.logEvent("portfolio_group_options_closed") reduce { state.copy(editGroupOptionsSheetState = null) } } fun onShowGroupRename(group: Group) = intent { + analyticsManager.logEvent("portfolio_group_rename_opened") reduce { state.copy(editGroupRenameSheetState = EditGroupRenameSheetState(group)) } } @@ -249,6 +259,7 @@ class PortfolioViewModel( group: Group, newName: String, ) = intent { + analyticsManager.logEvent("portfolio_group_renamed") val renamed = group.copy(name = newName) groupRepo.update(renamed, GroupFeatureType.Portfolio) val groups = groupRepo.getAllSorted(GroupFeatureType.Portfolio) @@ -266,6 +277,7 @@ class PortfolioViewModel( fun onDismissGroupRename() = intent { + analyticsManager.logEvent("portfolio_group_rename_closed") reduce { state.copy(editGroupRenameSheetState = null) } } diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt index 5feb681e9..cfda0dfba 100644 --- a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt @@ -2,6 +2,7 @@ package dev.arkbuilders.rate.feature.quick.presentation.add +import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box @@ -98,6 +99,10 @@ fun AddQuickScreen( } } + BackHandler { + viewModel.onBackClick() + } + val state by viewModel.collectAsState() viewModel.collectSideEffect { effect -> @@ -112,7 +117,7 @@ fun AddQuickScreen( R.string.quick_edit_calc AppTopBarBack( title = stringResource(title), - onBackClick = { navigator.popBackStack() }, + onBackClick = { viewModel.onBackClick() }, ) }, ) { diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt index 323c66bbd..8afa4f598 100644 --- a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt @@ -43,6 +43,8 @@ sealed class AddQuickScreenEffect { data class NavigateSearchAdd(val prohibitedCodes: List) : AddQuickScreenEffect() + + data object NavigateBack : AddQuickScreenEffect() } enum class SearchNavResultType { @@ -66,8 +68,6 @@ class AddQuickViewModel( container(AddQuickScreenState()) init { - analyticsManager.trackScreen("AddQuickScreen") - intent { val groups = groupRepo.getAllSorted(GroupFeatureType.Quick) val quickCalculation = quickCalculationId?.let { quickRepo.getById(it) } @@ -138,6 +138,7 @@ class AddQuickViewModel( fun onCurrencyRemove(removeIndex: Int) = intent { + analyticsManager.logEvent("add_quick_currency_removed") reduce { state.copy( currencies = @@ -150,11 +151,13 @@ class AddQuickViewModel( fun onGroupSelect(group: Group) = intent { + analyticsManager.logEvent("add_quick_group_selected") reduce { state.copy(group = group) } } fun onGroupCreate(name: String) = intent { + analyticsManager.logEvent("add_quick_group_created") val group = Group.empty(name = name) val inAvailable = state.availableGroups.any { it.name == group.name } reduce { @@ -185,6 +188,8 @@ class AddQuickViewModel( if (state.currencies.size < 2) return@intent + analyticsManager.logEvent("add_quick_swap_clicked") + val newFrom = state.currencies.last() val newCurrencies = state.currencies.toMutableList().apply { @@ -201,6 +206,7 @@ class AddQuickViewModel( from: Int, to: Int, ) = intent { + analyticsManager.logEvent("add_quick_currencies_reordered") val new = state.currencies.toMutableList().apply { add(to, removeAt(from)) @@ -220,6 +226,13 @@ class AddQuickViewModel( 0 } + val isNew = id == 0L + if (isNew) { + analyticsManager.logEvent("add_quick_calculation_added") + } else { + analyticsManager.logEvent("add_quick_calculation_edited") + } + val pinnedDate = if (id == quickCalculationId) { quickRepo.getById(id)?.pinnedDate @@ -283,6 +296,7 @@ class AddQuickViewModel( fun onSetCode(index: Int) = intent { + analyticsManager.logEvent("add_quick_set_code_clicked") val prohibitedCodes = state.currencies.map { it.code }.toMutableList().apply { removeAt(index) @@ -292,9 +306,16 @@ class AddQuickViewModel( fun onAddCode() = intent { + analyticsManager.logEvent("add_quick_add_code_clicked") val prohibitedCodes = state.currencies.map { it.code } postSideEffect(AddQuickScreenEffect.NavigateSearchAdd(prohibitedCodes)) } + + fun onBackClick() = + intent { + analyticsManager.logEvent("quick_back_clicked") + postSideEffect(AddQuickScreenEffect.NavigateBack) + } } class AddQuickViewModelFactory @AssistedInject constructor( diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/HandleAddQuickSideEffect.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/HandleAddQuickSideEffect.kt index 1b135eb5e..f342d3719 100644 --- a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/HandleAddQuickSideEffect.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/HandleAddQuickSideEffect.kt @@ -30,5 +30,7 @@ suspend fun handleAddQuickSideEffect( prohibitedCodes = effect.prohibitedCodes.toTypedArray(), ), ) + + AddQuickScreenEffect.NavigateBack -> navigator.popBackStack() } } diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickViewModel.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickViewModel.kt index 471b0ba1b..d8ab103f3 100644 --- a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickViewModel.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickViewModel.kt @@ -90,8 +90,6 @@ class QuickViewModel( container(QuickScreenState()) init { - analyticsManager.trackScreen("QuickScreen") - init() } @@ -176,33 +174,39 @@ class QuickViewModel( fun onShowGroupOptions(calculation: QuickCalculation) = intent { + analyticsManager.logEvent("quick_group_options_opened") reduce { state.copy(calculationOptionsData = CalculationOptionsData(calculation)) } } fun onHideOptions() = intent { + analyticsManager.logEvent("quick_group_options_closed") reduce { state.copy(calculationOptionsData = null) } } fun onPin(calculation: QuickCalculation) = intent { + analyticsManager.logEvent("quick_calculation_pinned") val newCalculation = calculation.copy(pinnedDate = OffsetDateTime.now()) quickRepo.insert(newCalculation) } fun onUnpin(calculation: QuickCalculation) = intent { + analyticsManager.logEvent("quick_calculation_unpinned") val newCalculation = calculation.copy(pinnedDate = null) quickRepo.insert(newCalculation) } fun onEdit(calc: QuickCalculation) = intent { + analyticsManager.logEvent("quick_calculation_edit_clicked") postSideEffect(QuickScreenEffect.NavigateToEdit(calc)) } fun onReuse(calc: QuickCalculation) = intent { + analyticsManager.logEvent("quick_calculation_reuse_clicked") postSideEffect(QuickScreenEffect.NavigateToReuse(calc)) } @@ -223,6 +227,7 @@ class QuickViewModel( fun onDelete(calculation: QuickCalculation) = intent { + analyticsManager.logEvent("quick_calculation_deleted") val deleted = quickRepo.delete(calculation.id) if (deleted) { postSideEffect(QuickScreenEffect.ShowRemovedSnackbar(calculation)) @@ -231,16 +236,19 @@ class QuickViewModel( fun undoDelete(calculation: QuickCalculation) = intent { + analyticsManager.logEvent("quick_calculation_undo_delete") quickRepo.insert(calculation) } fun onBackClick() = intent { if (state.filter.isNotEmpty()) { + analyticsManager.logEvent("quick_filter_cleared_via_back") reduce { state.copy(filter = "") } } else { + analyticsManager.logEvent("quick_back_clicked") postSideEffect(QuickScreenEffect.NavigateBack) } } @@ -292,6 +300,7 @@ class QuickViewModel( fun onShowGroupsReorder() = intent { + analyticsManager.logEvent("quick_group_reorder_opened") val groups = groupRepo.getAllSorted(GroupFeatureType.Quick) reduce { state.copy( @@ -304,6 +313,7 @@ class QuickViewModel( from: Int, to: Int, ) = intent { + analyticsManager.logEvent("quick_group_reordered") val newGroups = groupReorderSwapUseCase( state.editGroupReorderSheetState!!.groups, @@ -324,16 +334,19 @@ class QuickViewModel( fun onDismissGroupsReorder() = intent { + analyticsManager.logEvent("quick_group_reorder_closed") reduce { state.copy(editGroupReorderSheetState = null) } } fun onShowGroupOptions(group: Group) = intent { + analyticsManager.logEvent("quick_group_options_opened") reduce { state.copy(editGroupOptionsSheetState = EditGroupOptionsSheetState(group)) } } fun onGroupDelete(group: Group) = intent { + analyticsManager.logEvent("quick_group_deleted") groupRepo.delete(group.id) val groups = groupRepo.getAllSorted(GroupFeatureType.Quick) reduce { @@ -350,11 +363,13 @@ class QuickViewModel( fun onDismissGroupOptions() = intent { + analyticsManager.logEvent("quick_group_options_closed") reduce { state.copy(editGroupOptionsSheetState = null) } } fun onShowGroupRename(group: Group) = intent { + analyticsManager.logEvent("quick_group_rename_opened") reduce { state.copy(editGroupRenameSheetState = EditGroupRenameSheetState(group)) } } @@ -362,6 +377,7 @@ class QuickViewModel( group: Group, newName: String, ) = intent { + analyticsManager.logEvent("quick_group_renamed") val renamed = group.copy(name = newName) groupRepo.update(renamed, GroupFeatureType.Quick) val groups = groupRepo.getAllSorted(GroupFeatureType.Quick) @@ -379,6 +395,7 @@ class QuickViewModel( fun onDismissGroupRename() = intent { + analyticsManager.logEvent("quick_group_rename_closed") reduce { state.copy(editGroupRenameSheetState = null) } } diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt index 78e15c4bc..3b071294c 100644 --- a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt +++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt @@ -2,6 +2,7 @@ package dev.arkbuilders.rate.feature.search.presentation +import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -56,10 +57,16 @@ fun SearchCurrencyScreen( ) val state by viewModel.collectAsState() + BackHandler { + viewModel.onBackClick() + } + viewModel.collectSideEffect { effect -> when (effect) { is SearchScreenEffect.NavigateBackWithResult -> resultNavigator.navigateBack(effect.result) + + SearchScreenEffect.NavigateBack -> resultNavigator.navigateBack() } } @@ -75,7 +82,7 @@ fun SearchCurrencyScreen( topBar = { AppTopBarBack( title = title ?: stringResource(CoreRString.search_currency), - onBackClick = { resultNavigator.navigateBack() }, + onBackClick = { viewModel.onBackClick() }, ) }, ) { diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt index d73cdf92a..1523c7790 100644 --- a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt +++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt @@ -28,6 +28,8 @@ data class SearchScreenState( sealed class SearchScreenEffect { data class NavigateBackWithResult(val result: SearchNavResult) : SearchScreenEffect() + + data object NavigateBack : SearchScreenEffect() } class SearchViewModel( @@ -44,8 +46,6 @@ class SearchViewModel( container(SearchScreenState(prohibitedCodes = prohibitedCodes ?: emptyList())) init { - analyticsManager.trackScreen("SearchScreen") - intent { val all = currencyRepo.getCurrencyInfo() val frequent = calcFrequentCurrUseCase.invoke().map { currencyRepo.infoByCode(it) } @@ -87,6 +87,7 @@ class SearchViewModel( } val result = SearchNavResult(navKey, navPos, model.code) + analyticsManager.logEvent("search_back_clicked_with_result") postSideEffect(SearchScreenEffect.NavigateBackWithResult(result)) } @@ -94,6 +95,12 @@ class SearchViewModel( intent { reduce { state.copy(showCodeProhibitedDialog = false) } } + + fun onBackClick() = + intent { + analyticsManager.logEvent("search_back_clicked") + postSideEffect(SearchScreenEffect.NavigateBack) + } } class SearchViewModelFactory @AssistedInject constructor( diff --git a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsScreen.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsScreen.kt index 4038871db..7bb883106 100644 --- a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsScreen.kt +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsScreen.kt @@ -1,6 +1,7 @@ package dev.arkbuilders.rate.feature.settings.presentation import android.content.Context +import androidx.activity.compose.BackHandler import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -46,6 +47,7 @@ import dev.arkbuilders.rate.core.presentation.utils.DateFormatUtils import dev.arkbuilders.rate.feature.settings.di.SettingsComponentHolder import dev.arkbuilders.rate.feature.settings.domain.model.AppLanguage import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect import java.time.Duration import java.time.OffsetDateTime @@ -62,6 +64,19 @@ fun SettingsScreen(navigator: DestinationsNavigator) { val state by viewModel.collectAsState() + BackHandler { + viewModel.onBackClick() + } + + viewModel.collectSideEffect { effect -> + when (effect) { + SettingsScreenEffect.NavigateToAbout -> + navigator.navigate(AboutScreenDestination) + + SettingsScreenEffect.NavigateBack -> navigator.popBackStack() + } + } + Scaffold { Box(modifier = Modifier.padding(it)) { Content( @@ -71,6 +86,7 @@ fun SettingsScreen(navigator: DestinationsNavigator) { onAnalyticsToggle = viewModel::onAnalyticsToggle, onToggleLanguagePopup = viewModel::onToggleLanguagePopup, onChangeLanguage = viewModel::onChangeLanguage, + onAboutClick = viewModel::onAboutClick, ) } } @@ -84,6 +100,7 @@ private fun Content( onAnalyticsToggle: (Boolean) -> Unit, onToggleLanguagePopup: (Boolean) -> Unit, onChangeLanguage: (AppLanguage) -> Unit, + onAboutClick: () -> Unit, ) { val ctx = LocalContext.current Column( @@ -201,7 +218,7 @@ private fun Content( modifier = Modifier .fillMaxWidth() - .clickable { navigator.navigate(AboutScreenDestination) } + .clickable { onAboutClick() } .padding(horizontal = 16.dp, vertical = 20.dp), verticalAlignment = Alignment.CenterVertically, ) { diff --git a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsViewModel.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsViewModel.kt index c1117cf39..b2de2aff0 100644 --- a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsViewModel.kt +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsViewModel.kt @@ -35,7 +35,11 @@ data class SettingsScreenState( val showLanguagePopup: Boolean = false, ) -sealed class SettingsScreenEffect() +sealed class SettingsScreenEffect() { + data object NavigateToAbout : SettingsScreenEffect() + + data object NavigateBack : SettingsScreenEffect() +} class SettingsViewModel( private val prefs: Prefs, @@ -48,8 +52,6 @@ class SettingsViewModel( container(SettingsScreenState(showCrashReports = buildConfigFields.isGooglePlayBuild.not())) init { - analyticsManager.trackScreen("SettingsScreen") - intent { timestampRepo .timestampFlow(TimestampType.FetchRates) @@ -94,6 +96,13 @@ class SettingsViewModel( fun onAnalyticsToggle(enabled: Boolean) = intent { + analyticsManager.logEvent( + if (enabled) + "settings_analytics_enabled" + else + "settings_analytics_disabled", + ) + Firebase.analytics.setAnalyticsCollectionEnabled(enabled) prefs.set(PreferenceKey.CollectAnalytics, enabled) reduce { @@ -112,11 +121,24 @@ class SettingsViewModel( fun onChangeLanguage(language: AppLanguage) = intent { + analyticsManager.logEvent("settings_language_changed") languageRepo.setLanguage(language) reduce { state.copy(language = language) } } + + fun onAboutClick() = + intent { + analyticsManager.logEvent("settings_about_clicked") + postSideEffect(SettingsScreenEffect.NavigateToAbout) + } + + fun onBackClick() = + intent { + analyticsManager.logEvent("settings_back_clicked") + postSideEffect(SettingsScreenEffect.NavigateBack) + } } @SettingsScope