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 198cc7305b..6d18a10706 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 @@ -115,12 +115,18 @@ object EventsDictionary { const val goBack = "HistoryBack" const val bookmarkOpenUrl = "BlockBookmarkOpenUrl" - //Toolbars + // Toolbars const val slashMenu = "KeyboardBarSlashMenu" const val styleMenu = "KeyboardBarStyleMenu" const val selectionMenu = "KeyboardBarSelectionMenu" const val mentionMenu = "KeyboardBarMentionMenu" + // Library + const val libraryView = "LibraryView" + const val libraryScreenType = "ScreenType" + const val libraryScreenRelation = "ScreenRelation" + const val librarySetTypeName = "SetTypeName" + const val libraryCreateType = "CreateType" // Routes object Routes { diff --git a/analytics/src/main/java/com/anytypeio/anytype/analytics/tracker/AmplitudeTracker.kt b/analytics/src/main/java/com/anytypeio/anytype/analytics/tracker/AmplitudeTracker.kt index 2cc106c4ae..aaad07fca8 100644 --- a/analytics/src/main/java/com/anytypeio/anytype/analytics/tracker/AmplitudeTracker.kt +++ b/analytics/src/main/java/com/anytypeio/anytype/analytics/tracker/AmplitudeTracker.kt @@ -41,6 +41,7 @@ class AmplitudeTracker( renderTime = event.duration?.render ) tracker.logEvent(event.name, props) + Timber.d("Analytics Amplitude(event = $event)") } } } diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/library/LibraryDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/library/LibraryDI.kt index bd9836d29f..ac32e51a79 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/library/LibraryDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/library/LibraryDI.kt @@ -2,6 +2,7 @@ package com.anytypeio.anytype.di.feature.library import android.content.Context 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.base.AppCoroutineDispatchers @@ -193,4 +194,6 @@ interface LibraryDependencies : ComponentDependencies { fun channel(): SubscriptionEventChannel fun dispatchers(): AppCoroutineDispatchers fun userSettingsRepository(): UserSettingsRepository + + fun analytics(): Analytics } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeCreationDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeCreationDI.kt index 286b944ce8..4284f46014 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeCreationDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeCreationDI.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.di.feature.types 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.base.AppCoroutineDispatchers @@ -65,4 +66,5 @@ interface TypeCreationDependencies : ComponentDependencies { fun blockRepository(): BlockRepository fun dispatchers(): AppCoroutineDispatchers fun urlBuilder(): UrlBuilder + fun analytics(): Analytics } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeEditDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeEditDI.kt index e094fef0cf..134a37355a 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeEditDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/types/TypeEditDI.kt @@ -1,7 +1,7 @@ package com.anytypeio.anytype.di.feature.types import androidx.lifecycle.ViewModelProvider -import com.anytypeio.anytype.core_models.Id +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.block.repo.BlockRepository @@ -71,4 +71,5 @@ object TypeEditModule { interface TypeEditDependencies : ComponentDependencies { fun blockRepository(): BlockRepository fun urlBuilder(): UrlBuilder + fun analytics(): Analytics } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/library/views/LibraryTabs.kt b/app/src/main/java/com/anytypeio/anytype/ui/library/views/LibraryTabs.kt index 5a4302fc96..8fdf1ffb68 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/library/views/LibraryTabs.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/library/views/LibraryTabs.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.unit.dp import com.anytypeio.anytype.R +import com.anytypeio.anytype.presentation.library.LibraryEvent import com.anytypeio.anytype.ui.library.LibraryConfiguration import com.anytypeio.anytype.ui.library.LibraryScreenConfig import com.anytypeio.anytype.ui.library.ScreenState @@ -36,7 +37,7 @@ fun LibraryTabs( modifier: Modifier, pagerState: PagerState, configuration: LibraryConfiguration, - screenState: MutableState, + screenState: MutableState ) = WrapWithLibraryAnimation(visible = screenState.value.visible()) { val coroutineScope = rememberCoroutineScope() diff --git a/app/src/main/java/com/anytypeio/anytype/ui/library/views/list/LibraryListTabsContent.kt b/app/src/main/java/com/anytypeio/anytype/ui/library/views/list/LibraryListTabsContent.kt index 97c1d7675a..4f7eb98782 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/library/views/list/LibraryListTabsContent.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/library/views/list/LibraryListTabsContent.kt @@ -87,8 +87,22 @@ fun LibraryListTabsContent( userScrollEnabled = screenState.value == ScreenState.CONTENT ) { index -> val data = when (configuration[index]) { - is LibraryListConfig.Types, is LibraryListConfig.Relations -> tabs.my - is LibraryListConfig.TypesLibrary, is LibraryListConfig.RelationsLibrary -> tabs.lib + is LibraryListConfig.Types -> { + vmEventStream.invoke(LibraryEvent.Ui.ViewTypes) + tabs.my + } + is LibraryListConfig.Relations -> { + vmEventStream.invoke(LibraryEvent.Ui.ViewRelations) + tabs.my + } + is LibraryListConfig.TypesLibrary -> { + vmEventStream.invoke(LibraryEvent.Ui.ViewLibTypes) + tabs.lib + } + is LibraryListConfig.RelationsLibrary -> { + vmEventStream.invoke(LibraryEvent.Ui.ViewLibRelations) + tabs.lib + } } val itemsListState = rememberLazyListState() Column( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryEvents.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryEvents.kt index 2769734b6a..a32bfc1e93 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryEvents.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryEvents.kt @@ -4,8 +4,8 @@ sealed class LibraryEvent { sealed class BottomMenu : LibraryEvent() { class Back : BottomMenu() - class Search: BottomMenu() - class AddDoc: BottomMenu() + class Search : BottomMenu() + class AddDoc : BottomMenu() } sealed class Query(open val query: String) : LibraryEvent() { @@ -20,14 +20,26 @@ sealed class LibraryEvent { class Relation(override val item: LibraryView) : ToggleInstall(item) } - sealed class Type: LibraryEvent() { + sealed class Type : LibraryEvent() { class Create(val name: String = "") : Type() class Edit(val item: LibraryView.MyTypeView) : Type() } - sealed class Relation: LibraryEvent() { + sealed class Relation : LibraryEvent() { class Create(val name: String = "") : Relation() class Edit(val item: LibraryView.MyRelationView) : Relation() } -} \ No newline at end of file + sealed class Ui(val type: String, val view: String) : LibraryEvent() { + object ViewTypes : Ui(type = TypeType, view = ViewYour) + object ViewRelations : Ui(type = TypeRelation, view = ViewYour) + object ViewLibTypes : Ui(type = TypeType, view = ViewLibrary) + object ViewLibRelations : Ui(type = TypeRelation, view = ViewLibrary) + } + +} + +private const val TypeType = "type" +private const val TypeRelation = "relation" +private const val ViewYour = "your" +private const val ViewLibrary = "library" \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryViewModel.kt index 37aeebfd98..4cf3d0d736 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/library/LibraryViewModel.kt @@ -3,6 +3,12 @@ package com.anytypeio.anytype.presentation.library import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.anytypeio.anytype.analytics.base.Analytics +import com.anytypeio.anytype.analytics.base.EventsDictionary.libraryScreenRelation +import com.anytypeio.anytype.analytics.base.EventsDictionary.libraryScreenType +import com.anytypeio.anytype.analytics.base.EventsDictionary.libraryView +import com.anytypeio.anytype.analytics.base.sendEvent +import com.anytypeio.anytype.analytics.props.Props import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_utils.ext.orNull @@ -34,7 +40,8 @@ class LibraryViewModel( private val removeObjectsFromWorkspace: RemoveObjectsFromWorkspace, private val resourceManager: LibraryResourceManager, private val setObjectDetails: SetObjectDetails, - private val createObject: CreateObject + private val createObject: CreateObject, + private val analytics: Analytics ) : NavigationViewModel() { private val uiEvents = MutableStateFlow(LibraryEvent.Query.MyTypes("")) @@ -86,11 +93,25 @@ class LibraryViewModel( is LibraryEvent.Type -> proceedWithTypeActions(it) is LibraryEvent.Relation -> proceedWithRelationActions(it) is LibraryEvent.BottomMenu -> proceedWithBottomMenuActions(it) + is LibraryEvent.Ui -> proceedWithViewAnalytics(it) } } } } + private fun proceedWithViewAnalytics(it: LibraryEvent.Ui) { + viewModelScope.sendEvent( + analytics = analytics, + eventName = libraryView, + props = Props( + mapOf( + "type" to it.type, + "view" to it.view + ) + ) + ) + } + private fun proceedWithBottomMenuActions(it: LibraryEvent.BottomMenu) { when (it) { is LibraryEvent.BottomMenu.Back -> navigate(Navigation.Back()) @@ -104,7 +125,7 @@ class LibraryViewModel( createObject.execute(CreateObject.Param(type = null)) .fold( onSuccess = { result -> - navigate(Navigation.CreateDoc(result.objectId)) + navigate(Navigation.CreateDoc(result.objectId)) }, onFailure = { e -> Timber.e(e, "Error while creating a new page") } ) @@ -137,15 +158,35 @@ class LibraryViewModel( private fun proceedWithTypeActions(event: LibraryEvent.Type) { when (event) { - is LibraryEvent.Type.Create -> navigate(Navigation.OpenTypeCreation(event.name)) - is LibraryEvent.Type.Edit -> navigate(Navigation.OpenTypeEditing(event.item)) + is LibraryEvent.Type.Create -> { + navigate(Navigation.OpenTypeCreation(event.name)) + } + is LibraryEvent.Type.Edit -> { + viewModelScope.sendEvent( + analytics = analytics, + eventName = libraryScreenType, + props = Props( + map = mapOf("objectType" to event.item.id) + ) + ) + navigate(Navigation.OpenTypeEditing(event.item)) + } } } private fun proceedWithRelationActions(event: LibraryEvent.Relation) { when (event) { is LibraryEvent.Relation.Create -> navigate(Navigation.OpenRelationCreation(event.name)) - is LibraryEvent.Relation.Edit -> navigate(Navigation.OpenRelationEditing(event.item)) + is LibraryEvent.Relation.Edit -> { + viewModelScope.sendEvent( + analytics = analytics, + eventName = libraryScreenRelation, + props = Props( + map = mapOf("relationKey" to event.item.id) + ) + ) + navigate(Navigation.OpenRelationEditing(event.item)) + } } } @@ -308,7 +349,8 @@ class LibraryViewModel( private val removeObjectsFromWorkspace: RemoveObjectsFromWorkspace, private val resourceManager: LibraryResourceManager, private val setObjectDetails: SetObjectDetails, - private val createObject: CreateObject + private val createObject: CreateObject, + private val analytics: Analytics ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { @@ -321,7 +363,8 @@ class LibraryViewModel( removeObjectsFromWorkspace, resourceManager, setObjectDetails, - createObject + createObject, + analytics ) as T } } @@ -345,9 +388,9 @@ class LibraryViewModel( class Back : Navigation() - class Search: Navigation() + class Search : Navigation() - class CreateDoc(val id: Id): Navigation() + class CreateDoc(val id: Id) : Navigation() } sealed class Effect { diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeCreationViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeCreationViewModel.kt index a3ca640d54..2e0f0290aa 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeCreationViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeCreationViewModel.kt @@ -3,6 +3,9 @@ package com.anytypeio.anytype.presentation.types import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.anytypeio.anytype.analytics.base.Analytics +import com.anytypeio.anytype.analytics.base.EventsDictionary.libraryCreateType +import com.anytypeio.anytype.analytics.base.sendEvent import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectWrapper @@ -26,7 +29,8 @@ import timber.log.Timber class TypeCreationViewModel( private val createTypeInteractor: CreateType, private val urlBuilder: UrlBuilder, - private val emojiProvider: EmojiProvider + private val emojiProvider: EmojiProvider, + private val analytics: Analytics ) : NavigationViewModel() { private val unicodeIconFlow = MutableStateFlow("") @@ -59,6 +63,7 @@ class TypeCreationViewModel( ) ).fold( onSuccess = { + viewModelScope.sendEvent(analytics = analytics, eventName = libraryCreateType) navigate(Navigation.BackWithCreatedType) }, onFailure = { @@ -96,14 +101,16 @@ class TypeCreationViewModel( class Factory @Inject constructor( private val createType: CreateType, private val urlBuilder: UrlBuilder, - private val emojiProvider: EmojiProvider + private val emojiProvider: EmojiProvider, + private val analytics: Analytics ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { return TypeCreationViewModel( createTypeInteractor = createType, urlBuilder = urlBuilder, - emojiProvider = emojiProvider + emojiProvider = emojiProvider, + analytics = analytics ) as T } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeEditViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeEditViewModel.kt index 3fa47b5e1b..510db3606d 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeEditViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/types/TypeEditViewModel.kt @@ -3,6 +3,9 @@ package com.anytypeio.anytype.presentation.types import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.anytypeio.anytype.analytics.base.Analytics +import com.anytypeio.anytype.analytics.base.EventsDictionary.librarySetTypeName +import com.anytypeio.anytype.analytics.base.sendEvent import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectWrapper @@ -23,7 +26,8 @@ class TypeEditViewModel( private val urlBuilder: UrlBuilder, private val id: Id, private val name: String, - private val icon: String + private val icon: String, + private val analytics: Analytics ) : NavigationViewModel() { private val unicodeIconFlow = MutableStateFlow(icon) @@ -68,6 +72,7 @@ class TypeEditViewModel( } fun updateObjectDetails(name: String) { + viewModelScope.sendEvent(analytics = analytics, eventName = librarySetTypeName) navigate(Navigation.BackWithModify(id, name, unicodeIconFlow.value)) } @@ -86,7 +91,8 @@ class TypeEditViewModel( private val urlBuilder: UrlBuilder, @TypeId private val id: Id, @TypeName private val name: String, - @TypeIcon private val icon: String + @TypeIcon private val icon: String, + private val analytics: Analytics ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { @@ -94,7 +100,8 @@ class TypeEditViewModel( urlBuilder = urlBuilder, id = id, name = name, - icon = icon + icon = icon, + analytics = analytics ) as T } }