diff --git a/analytics/src/main/java/com/anytypeio/anytype/analytics/base/EventsDictionary.kt b/analytics/src/main/java/com/anytypeio/anytype/analytics/base/EventsDictionary.kt index 0a8393d920..276883f683 100644 --- a/analytics/src/main/java/com/anytypeio/anytype/analytics/base/EventsDictionary.kt +++ b/analytics/src/main/java/com/anytypeio/anytype/analytics/base/EventsDictionary.kt @@ -20,8 +20,9 @@ object EventsDictionary { // Settings events const val wallpaperSet = "SettingsWallpaperSet" const val keychainCopy = "KeychainCopy" - const val defaultTypeChanged = "DefaultTypeChanged" + const val defaultTypeChanged = "DefaultTypeChange" const val fileOffloadSuccess = "FileOffload" + const val changeTheme = "ThemeSet" // Screen show events const val objectScreenShow = "ScreenObject" @@ -39,15 +40,16 @@ object EventsDictionary { const val manageFilesScreenShow = "ScreenManageFiles" const val relationsScreenShow = "ScreenObjectRelation" const val settingsShow = "ScreenSettings" - const val personalisationSettingsShow = "ScreenSettingsPersonalisation" + const val personalisationSettingsShow = "ScreenSettingsPersonal" const val wallpaperScreenShow = "ScreenSettingsWallpaper" - const val accountDataSettingsShow = "ScreenSettingsAccountData" + const val accountDataSettingsShow = "ScreenSettingsAccount" const val aboutScreenShow = "ScreenSettingsAbout" const val appearanceScreenShow = "ScreenSettingsAppearance" const val screenSettingsStorage = "ScreenSettingsStorageIndex" const val screenSettingsStorageManage = "ScreenSettingsStorageManager" const val screenSettingsStorageOffload = "ScreenFileOffloadWarning" const val settingsStorageOffload = "SettingsStorageOffload" + const val screenSettingsDelete = "ScreenSettingsDelete" // Object events const val objectListDelete = "RemoveCompletely" @@ -74,6 +76,7 @@ object EventsDictionary { const val objectSetDescription = "SetObjectDescription" const val objectOpenAs = "OpenAsObject " const val objectReload = "ReloadSourceData" + const val objectDuplicate = "DuplicateObject" // Blocks events const val blockCreate = "CreateBlock" @@ -88,6 +91,7 @@ object EventsDictionary { const val blockReorder = "ReorderBlock" const val blockUpload = "UploadMedia" const val blockDownload = "DownloadMedia" + const val blockMove = "MoveBlock" // Relations const val relationAdd = "AddExistingRelation" @@ -120,6 +124,7 @@ object EventsDictionary { const val goBack = "HistoryBack" const val bookmarkOpenUrl = "BlockBookmarkOpenUrl" + const val hideKeyboard = "KeyboardBarHideKeyboardMenu" // Toolbars const val slashMenu = "KeyboardBarSlashMenu" diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt index bad95eb4b7..a2b61bb15d 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.di.feature.settings import androidx.lifecycle.ViewModelProvider +import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_utils.di.scope.PerScreen import com.anytypeio.anytype.di.common.ComponentDependencies import com.anytypeio.anytype.domain.config.UserSettingsRepository @@ -63,4 +64,5 @@ object AppearanceModule { interface AppearanceDependencies : ComponentDependencies { fun userUserSettingsRepository(): UserSettingsRepository + fun analytics(): Analytics } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileFragment.kt index 876593ce78..cc221dce9d 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileFragment.kt @@ -95,6 +95,7 @@ class ProfileFragment : BaseBottomSheetComposeFragment() { } private fun proceedWithAccountDeletion() { + vm.proceedWithAccountDeletion() val dialog = DeleteAccountWarning() dialog.onDeletionAccepted = { dialog.dismiss() diff --git a/app/src/main/res/layout/fragment_object_menu.xml b/app/src/main/res/layout/fragment_object_menu.xml index 94ec9cb16d..6a2cbd7c50 100644 --- a/app/src/main/res/layout/fragment_object_menu.xml +++ b/app/src/main/res/layout/fragment_object_menu.xml @@ -1,5 +1,4 @@ - - "italic" - Markup.Type.BOLD -> "bold" - Markup.Type.STRIKETHROUGH -> "strikethrough" - Markup.Type.TEXT_COLOR -> "color" - Markup.Type.BACKGROUND_COLOR -> "bgcolor" - Markup.Type.LINK -> "linkURL" - Markup.Type.KEYBOARD -> "code" - Markup.Type.MENTION -> "mention" - Markup.Type.OBJECT -> "linkObject" - Markup.Type.UNDERLINE -> "underline" + Markup.Type.ITALIC -> "Italic" + Markup.Type.BOLD -> "Bold" + Markup.Type.STRIKETHROUGH -> "Strike" + Markup.Type.TEXT_COLOR -> "Color" + Markup.Type.BACKGROUND_COLOR -> "BgColor" + Markup.Type.LINK -> "Link" + Markup.Type.KEYBOARD -> "Code" + Markup.Type.MENTION -> "Mention" + Markup.Type.OBJECT -> "Object" + Markup.Type.UNDERLINE -> "Underline" } fun DVViewerType.getPropName() = when (this) { @@ -404,6 +406,18 @@ fun CoroutineScope.sendAnalyticsMoveToBinEvent( ) } +fun CoroutineScope.sendAnalyticsDuplicateEvent( + analytics: Analytics, + startTime: Long +) { + sendEvent( + analytics = analytics, + eventName = objectDuplicate, + startTime = startTime, + middleTime = System.currentTimeMillis() + ) +} + suspend fun Analytics.sendAnalyticsSplitBlockEvent( style: TextStyle, startTime: Long, middlewareTime: Long ) { @@ -700,6 +714,17 @@ fun CoroutineScope.sendAnalyticsSetTitleEvent( ) } +fun CoroutineScope.sendAnalyticsBlockMoveToEvent( + analytics: Analytics, + count: Int +) { + sendEvent( + analytics = analytics, + eventName = EventsDictionary.blockMove, + props = Props(mapOf(EventsPropertiesKey.count to count)) + ) +} + fun CoroutineScope.sendAnalyticsSetDescriptionEvent( analytics: Analytics, context: String? = null @@ -1530,4 +1555,28 @@ suspend fun Analytics.sendDeletionWarning() { sendEvent( eventName = EventsDictionary.deletionWarningShow ) +} + +suspend fun Analytics.sendScreenSettingsDeleteEvent() { + sendEvent( + eventName = EventsDictionary.screenSettingsDelete + ) +} + +suspend fun Analytics.sendChangeThemeEvent(theme: ThemeMode) { + val name = when (theme) { + ThemeMode.Light -> "light" + ThemeMode.Night -> "dark" + ThemeMode.System -> "system" + } + sendEvent( + eventName = EventsDictionary.changeTheme, + props = Props(map = mapOf("id" to name)) + ) +} + +suspend fun Analytics.sendHideKeyboardEvent() { + sendEvent( + eventName = EventsDictionary.hideKeyboard + ) } \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt index d7b59062da..bd05df7671 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/objects/menu/ObjectMenuViewModelBase.kt @@ -3,6 +3,7 @@ package com.anytypeio.anytype.presentation.objects.menu import android.net.Uri import androidx.lifecycle.viewModelScope import com.anytypeio.anytype.analytics.base.Analytics +import com.anytypeio.anytype.analytics.base.EventsDictionary import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.Payload @@ -20,6 +21,7 @@ import com.anytypeio.anytype.presentation.common.Delegator import com.anytypeio.anytype.presentation.extension.sendAnalyticsAddToCollectionEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsAddToFavoritesEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsBackLinkAddEvent +import com.anytypeio.anytype.presentation.extension.sendAnalyticsDuplicateEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsMoveToBinEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsRemoveFromFavoritesEvent import com.anytypeio.anytype.presentation.objects.ObjectAction @@ -292,6 +294,7 @@ abstract class ObjectMenuViewModelBase( fun proceedWithDuplication(ctx: Id) { Timber.d("proceedWithDuplication, ctx:[$ctx]") + val startTime = System.currentTimeMillis() viewModelScope.launch { duplicateObject(ctx).process( failure = { @@ -301,6 +304,10 @@ abstract class ObjectMenuViewModelBase( success = { _toasts.emit("Your object is duplicated") delegator.delegate(Action.Duplicate(it)) + sendAnalyticsDuplicateEvent( + analytics = analytics, + startTime = startTime + ) } ) } diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileViewModel.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileViewModel.kt index 9613a31761..42378ef239 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileViewModel.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileViewModel.kt @@ -18,6 +18,7 @@ import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.`object`.SetObjectDetails +import com.anytypeio.anytype.presentation.extension.sendScreenSettingsDeleteEvent import com.anytypeio.anytype.presentation.profile.ProfileIconView import com.anytypeio.anytype.presentation.profile.profileIcon import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider @@ -159,6 +160,12 @@ class ProfileViewModel( } } + fun proceedWithAccountDeletion() { + viewModelScope.launch { + analytics.sendScreenSettingsDeleteEvent() + } + } + sealed class AccountProfile { object Idle: AccountProfile() diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt index 2afa3ecd64..53fac16669 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt @@ -3,10 +3,12 @@ package com.anytypeio.anytype.ui_settings.appearance import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_models.ThemeMode import com.anytypeio.anytype.domain.base.BaseUseCase import com.anytypeio.anytype.domain.theme.GetTheme import com.anytypeio.anytype.domain.theme.SetTheme +import com.anytypeio.anytype.presentation.extension.sendChangeThemeEvent import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import timber.log.Timber @@ -16,6 +18,7 @@ class AppearanceViewModel( private val getTheme: GetTheme, private val setTheme: SetTheme, private val themeApplicator: ThemeApplicator, + private val analytics: Analytics ) : ViewModel() { val selectedTheme = MutableStateFlow(ThemeMode.System) @@ -33,6 +36,9 @@ class AppearanceViewModel( } private fun saveTheme(mode: ThemeMode) { + viewModelScope.launch { + analytics.sendChangeThemeEvent(mode) + } viewModelScope.launch { setTheme(params = mode).proceed( success = { @@ -70,13 +76,15 @@ class AppearanceViewModel( private val getTheme: GetTheme, private val setTheme: SetTheme, private val themeApplicator: ThemeApplicator, + private val analytics: Analytics ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { return AppearanceViewModel( getTheme = getTheme, setTheme = setTheme, - themeApplicator = themeApplicator + themeApplicator = themeApplicator, + analytics = analytics ) as T } }