From 65845ec9abde9c55e8d1988af809ed7a333984eb Mon Sep 17 00:00:00 2001 From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com> Date: Mon, 27 Mar 2023 12:59:59 +0200 Subject: [PATCH] DROID-1027 Collections | Object creation, start params (#3042) * DROID-1027 types widget should be in hidden state on opening * DROID-1027 update type widget showing logic + tests * DROID-1027 send event on create object with template * DROID-1027 delete doubled logic * DROID-1027 createObject, internal flags + tests * DROID-1027 fix test --- .../anytype/domain/page/CreateObject.kt | 19 +++-- .../anytype/domain/page/CreateObjectTest.kt | 10 ++- .../presentation/editor/EditorViewModel.kt | 36 +++------ .../presentation/home/HomeScreenViewModel.kt | 27 ++++++- .../EditorObjectTypeChangeWidgetTest.kt | 79 ++----------------- .../home/HomeScreenViewModelTest.kt | 7 +- 6 files changed, 65 insertions(+), 113 deletions(-) diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateObject.kt b/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateObject.kt index 36c19f8184..c024f916d5 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateObject.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateObject.kt @@ -25,16 +25,23 @@ class CreateObject( val type = params.type ?: getDefaultPageType.run(Unit).type - val template = if (type != null) { - getTemplates.run(GetTemplates.Params(type = type)).singleOrNull()?.id + val objectTemplates = if (type != null) { + getTemplates.run(GetTemplates.Params(type = type)) } else { null } - val internalFlags = if (template != null) { - listOf() - } else { - listOf(InternalFlags.ShouldSelectType, InternalFlags.ShouldEmptyDelete) + val template = objectTemplates?.singleOrNull()?.id + + val internalFlags = buildList { + if (objectTemplates != null && objectTemplates.size > 1) { + add(InternalFlags.ShouldSelectTemplate) + } else { + if (template == null) { + add(InternalFlags.ShouldSelectType) + } + } + add(InternalFlags.ShouldEmptyDelete) } val prefilled = buildMap { diff --git a/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateObjectTest.kt b/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateObjectTest.kt index 0abc692333..f8aea948dd 100644 --- a/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateObjectTest.kt +++ b/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateObjectTest.kt @@ -126,7 +126,9 @@ class CreateObjectTest { val commands = Command.CreateObject( prefilled = buildMap { put(Relations.TYPE, defaultType) }, template = templateBook, - internalFlags = listOf() + internalFlags = listOf( + InternalFlags.ShouldEmptyDelete + ) ) verifyBlocking(repo, times(1)) { createObject(commands) } } @@ -180,7 +182,9 @@ class CreateObjectTest { val commands = Command.CreateObject( prefilled = buildMap { put(Relations.TYPE, type) }, template = template, - internalFlags = listOf() + internalFlags = listOf( + InternalFlags.ShouldEmptyDelete + ) ) verifyBlocking(repo, times(1)) { createObject(commands) } } @@ -216,7 +220,7 @@ class CreateObjectTest { prefilled = buildMap { put(Relations.TYPE, type) }, template = null, internalFlags = listOf( - InternalFlags.ShouldSelectType, + InternalFlags.ShouldSelectTemplate, InternalFlags.ShouldEmptyDelete ) ) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index ee9f02595f..561a3c7962 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -16,6 +16,7 @@ import com.anytypeio.anytype.core_models.Block.Prototype import com.anytypeio.anytype.core_models.Document import com.anytypeio.anytype.core_models.Event import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.core_models.InternalFlags import com.anytypeio.anytype.core_models.Key import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectTypeIds @@ -522,8 +523,8 @@ class EditorViewModel( orchestrator.stores.objectTypes.update(event.objectTypes) orchestrator.stores.relationLinks.update(event.relationLinks) orchestrator.stores.objectRestrictions.update(event.objectRestrictions) - val objectType = event.details.details[context]?.type?.firstOrNull() - proceedWithShowingObjectTypesWidget(objectType, event.blocks) + + proceedWithShowingObjectTypesWidget() } is Event.Command.Details -> { orchestrator.stores.details.apply { update(current().process(event)) } @@ -5831,8 +5832,6 @@ class EditorViewModel( fun onObjectTypesWidgetItemClicked(typeId: Id) { Timber.d("onObjectTypesWidgetItemClicked, type:[$typeId]") - dispatchObjectCreateEvent(typeId) - proceedWithHidingObjectTypeWidget() proceedWithObjectTypeChange(type = typeId, applyTemplate = true) } @@ -5852,32 +5851,17 @@ class EditorViewModel( ) } - private fun proceedWithShowingObjectTypesWidget(objectType: String?, blocks: List) { + private fun proceedWithShowingObjectTypesWidget() { val restrictions = orchestrator.stores.objectRestrictions.current() if (restrictions.contains(ObjectRestriction.TYPE_CHANGE)) { return } - when (objectType) { - ObjectTypeIds.NOTE -> { - val root = blocks.find { it.id == context } ?: return - if (root.children.size == 2) { - val lastBlock = blocks.find { it.id == root.children.last() } - if (lastBlock != null && lastBlock.content is Content.Text) { - if (lastBlock.content().text.isEmpty()) { - proceedWithGettingObjectTypesForObjectTypeWidget() - } - } - } - } - else -> { - val root = blocks.find { it.id == context } ?: return - if (root.children.size == 1) { - val title = blocks.title() ?: return - if (title.content().text.isEmpty()) { - proceedWithGettingObjectTypesForObjectTypeWidget() - } - } - } + val details = orchestrator.stores.details.current() + val objectDetails = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap()) + + val internalFlags = objectDetails.internalFlags + if (internalFlags.contains(InternalFlags.ShouldSelectType)) { + proceedWithGettingObjectTypesForObjectTypeWidget() } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt index c88562076c..306dceaaaf 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt @@ -3,6 +3,8 @@ package com.anytypeio.anytype.presentation.home 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 import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.Config import com.anytypeio.anytype.core_models.Event @@ -37,6 +39,7 @@ import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.widgets.CreateWidget import com.anytypeio.anytype.domain.widgets.DeleteWidget import com.anytypeio.anytype.domain.widgets.UpdateWidget +import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEvent import com.anytypeio.anytype.presentation.home.Command.ChangeWidgetType.Companion.UNDEFINED_LAYOUT_CODE import com.anytypeio.anytype.presentation.navigation.NavigationViewModel import com.anytypeio.anytype.presentation.search.Subscriptions @@ -95,7 +98,8 @@ class HomeScreenViewModel( private val emptyBin: EmptyBin, private val unsubscriber: Unsubscriber, private val getDefaultPageType: GetDefaultPageType, - private val appActionManager: AppActionManager + private val appActionManager: AppActionManager, + private val analytics: Analytics ) : NavigationViewModel(), Reducer, WidgetActiveViewStateHolder by widgetActiveViewStateHolder, @@ -728,10 +732,23 @@ class HomeScreenViewModel( } fun onCreateNewObjectClicked() { + val startTime = System.currentTimeMillis() viewModelScope.launch { createObject.stream(CreateObject.Param(null)).collect { result -> result.fold( - onSuccess = { navigate(Navigation.OpenObject(it.objectId)) }, + onSuccess = { + if (it.appliedTemplate != null) { + val middleTime = System.currentTimeMillis() + sendAnalyticsObjectCreateEvent( + analytics = analytics, + objType = it.type, + route = EventsDictionary.Routes.objPowerTool, + startTime = startTime, + middleTime = middleTime + ) + } + navigate(Navigation.OpenObject(it.objectId)) + }, onFailure = { Timber.e(it, "Error while creating object") sendToast("Error while creating object. Please, try again later") @@ -818,7 +835,8 @@ class HomeScreenViewModel( private val emptyBin: EmptyBin, private val unsubscriber: Unsubscriber, private val getDefaultPageType: GetDefaultPageType, - private val appActionManager: AppActionManager + private val appActionManager: AppActionManager, + private val analytics: Analytics ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T = HomeScreenViewModel( @@ -843,7 +861,8 @@ class HomeScreenViewModel( emptyBin = emptyBin, unsubscriber = unsubscriber, getDefaultPageType = getDefaultPageType, - appActionManager = appActionManager + appActionManager = appActionManager, + analytics = analytics ) as T } diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorObjectTypeChangeWidgetTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorObjectTypeChangeWidgetTest.kt index ac7448f102..60327358e7 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorObjectTypeChangeWidgetTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorObjectTypeChangeWidgetTest.kt @@ -3,8 +3,10 @@ package com.anytypeio.anytype.presentation.editor.editor import android.util.Log import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.InternalFlags import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectTypeIds +import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.SmartBlockType import com.anytypeio.anytype.presentation.editor.EditorViewModel import com.anytypeio.anytype.presentation.util.CoroutinesTestRule @@ -46,7 +48,7 @@ class EditorObjectTypeChangeWidgetTest : EditorPresentationTestSetup() { } @Test - fun `should show widget on note object with one empty text block`() { + fun `should show widget on object with select type internal flag`() { val paragraph = Block( id = MockDataFactory.randomUuid(), @@ -87,7 +89,8 @@ class EditorObjectTypeChangeWidgetTest : EditorPresentationTestSetup() { val objectDetails = Block.Fields( mapOf( "type" to ObjectTypeIds.NOTE, - "layout" to ObjectType.Layout.NOTE.code.toDouble() + "layout" to ObjectType.Layout.NOTE.code.toDouble(), + Relations.INTERNAL_FLAGS to listOf(1.0) ) ) @@ -116,7 +119,7 @@ class EditorObjectTypeChangeWidgetTest : EditorPresentationTestSetup() { } @Test - fun `should not show widget on note object with one not empty text block`() { + fun `should not show widget on note object with empty internal flags`() { val paragraph = Block( id = MockDataFactory.randomUuid(), @@ -183,74 +186,4 @@ class EditorObjectTypeChangeWidgetTest : EditorPresentationTestSetup() { assertNotNull(objectTypesWidget) assertFalse(objectTypesWidget.isVisible) } - - @Test - fun `should show widget on note object layout with one empty text block and default not note type`() { - - val paragraph = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.Text( - text = "", - marks = emptyList(), - style = Block.Content.Text.Style.P - ) - ) - - val featuredBlock = Block( - id = "featuredRelations", - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.FeaturedRelations - ) - - val header = Block( - id = "header", - content = Block.Content.Layout( - type = Block.Content.Layout.Type.HEADER - ), - fields = Block.Fields.empty(), - children = listOf(featuredBlock.id) - ) - - val page = Block( - id = root, - fields = Block.Fields(emptyMap()), - content = Block.Content.Smart(SmartBlockType.PAGE), - children = listOf(header.id, paragraph.id) - ) - - val doc = listOf(page, header, paragraph, featuredBlock) - - val objectDetails = Block.Fields( - mapOf( - "type" to ObjectTypeIds.NOTE, - "layout" to ObjectType.Layout.NOTE.code.toDouble() - ) - ) - - val detailsList = Block.Details(details = mapOf(root to objectDetails)) - - stubInterceptEvents() - stubInterceptThreadStatus() - stubSearchObjects() - stubGetDefaultObjectType(type = ObjectTypeIds.PAGE) - stubOpenDocument( - document = doc, - details = detailsList - ) - stubGetObjectTypes(listOf()) - - val vm = buildViewModel() - - vm.onStart(root) - - val state = vm.controlPanelViewState.value - - val objectTypesWidget = state?.objectTypesToolbar - - assertNotNull(objectTypesWidget) - assertTrue(objectTypesWidget.isVisible) - } } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt index 10d4705b7c..812ea9bbe7 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.presentation.home import app.cash.turbine.test +import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.Event import com.anytypeio.anytype.core_models.Id @@ -132,6 +133,9 @@ class HomeScreenViewModelTest { @Mock lateinit var gateway: Gateway + @Mock + lateinit var analytics: Analytics + private val objectPayloadDispatcher = Dispatcher.Default() private val widgetEventDispatcher = Dispatcher.Default() @@ -1883,7 +1887,8 @@ class HomeScreenViewModelTest { emptyBin = emptyBin, unsubscriber = unsubscriber, getDefaultPageType = getDefaultPageType, - appActionManager = appActionManager + appActionManager = appActionManager, + analytics = analytics ) companion object {