1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-1593 Editor | Feature | InternalFlags, selectTemplate, selectType (#261)

This commit is contained in:
Konstantin Ivanov 2023-08-08 22:24:51 +02:00 committed by GitHub
parent 25ec3e9a0e
commit a1a3b4f965
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 442 additions and 79 deletions

View file

@ -60,6 +60,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultStoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
@ -269,6 +270,9 @@ open class EditorTestSetup {
@Mock
lateinit var fileLimitsEventChannel: FileLimitsEventChannel
@Mock
lateinit var setObjectInternalFlags: SetObjectInternalFlags
lateinit var interceptFileLimitEvents: InterceptFileLimitEvents
lateinit var addRelationToObject: AddRelationToObject
@ -466,7 +470,8 @@ open class EditorTestSetup {
getObjectTypes = getObjectTypes,
objectToCollection = objectToCollection,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)
}

View file

@ -56,6 +56,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.DuplicateObject
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
@ -233,6 +234,14 @@ object EditorSessionModule {
dispatchers: AppCoroutineDispatchers
) : InterceptFileLimitEvents = InterceptFileLimitEvents(channel, dispatchers)
@JvmStatic
@Provides
@PerScreen
fun provideSetObjectInternalFlags(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : SetObjectInternalFlags = SetObjectInternalFlags(repo, dispatchers)
@JvmStatic
@Provides
fun providePageViewModelFactory(
@ -271,7 +280,8 @@ object EditorSessionModule {
getObjectTypes: GetObjectTypes,
objectToCollection: ConvertObjectToCollection,
interceptFileLimitEvents: InterceptFileLimitEvents,
addRelationToObject: AddRelationToObject
addRelationToObject: AddRelationToObject,
setObjectInternalFlags: SetObjectInternalFlags
): EditorViewModelFactory = EditorViewModelFactory(
openPage = openPage,
closeObject = closePage,
@ -308,7 +318,8 @@ object EditorSessionModule {
getObjectTypes = getObjectTypes,
objectToCollection = objectToCollection,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)

View file

@ -5,9 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
@ -20,7 +18,6 @@ import com.anytypeio.anytype.databinding.FragmentTemplateSelectBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.templates.TemplateSelectViewModel
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch
import javax.inject.Inject
class TemplateSelectFragment :
@ -40,9 +37,15 @@ class TemplateSelectFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupViewPagerAndTabs()
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
setupClickEventHandlers()
with(lifecycleScope) {
subscribe(binding.btnCancel.clicks()) {
vm.onCancelButtonClicked()
}
subscribe(binding.btnUseTemplate.clicks()) {
vm.onUseTemplateButtonPressed(
currentItem = binding.templateViewPager.currentItem,
ctx = ctx
)
}
}
}
@ -53,11 +56,6 @@ class TemplateSelectFragment :
TabLayoutMediator(binding.tabs, binding.templateViewPager) { _, _ -> }.attach()
}
private suspend fun setupClickEventHandlers() {
setupUseTemplateClicks()
setupCancelClicks()
}
override fun onStart() {
jobs += lifecycleScope.subscribe(vm.viewState) { render(it) }
jobs += lifecycleScope.subscribe(vm.isDismissed) { if (it) exit() }
@ -67,7 +65,6 @@ class TemplateSelectFragment :
private fun render(viewState: TemplateSelectViewModel.ViewState) {
when (viewState) {
TemplateSelectViewModel.ViewState.ErrorGettingType -> TODO()
TemplateSelectViewModel.ViewState.Init -> {
binding.tvTemplateCountOrTutorial.text = null
binding.btnCancel.isEnabled = true
@ -86,19 +83,6 @@ class TemplateSelectFragment :
}
}
private suspend fun setupUseTemplateClicks() {
binding.btnUseTemplate.clicks().collect {
vm.onUseTemplateButtonPressed(
currentItem = binding.templateViewPager.currentItem,
ctx = ctx
)
}
}
private suspend fun setupCancelClicks() {
binding.btnCancel.clicks().collect { exit() }
}
private fun exit() {
findNavController().popBackStack()
}

View file

@ -421,4 +421,6 @@ sealed class Command {
data class AddObjectToCollection(val ctx: Id, val afterId: Id, val ids: List<Id>)
data class SetQueryToSet(val ctx: Id, val query: String)
data class SetInternalFlags(val ctx: Id, val flags: List<InternalFlags>)
}

View file

@ -841,4 +841,8 @@ class BlockDataRepository(
override suspend fun fileSpaceUsage(): FileLimits {
return remote.fileSpaceUsage()
}
override suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload {
return remote.setInternalFlags(command)
}
}

View file

@ -360,4 +360,6 @@ interface BlockRemote {
suspend fun addObjectToCollection(command: Command.AddObjectToCollection): Payload
suspend fun setQueryToSet(command: Command.SetQueryToSet): Payload
suspend fun fileSpaceUsage(): FileLimits
suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload
}

View file

@ -412,4 +412,5 @@ interface BlockRepository {
suspend fun addObjectToCollection(command: Command.AddObjectToCollection): Payload
suspend fun setQueryToSet(command: Command.SetQueryToSet): Payload
suspend fun fileSpaceUsage(): FileLimits
suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload
}

View file

@ -0,0 +1,28 @@
package com.anytypeio.anytype.domain.`object`
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.InternalFlags
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class SetObjectInternalFlags(
private val repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<SetObjectInternalFlags.Params, Payload>(dispatchers.io) {
override suspend fun doWork(params: Params): Payload {
val command = Command.SetInternalFlags(
ctx = params.ctx,
flags = params.flags
)
return repo.setInternalFlags(command)
}
data class Params(
val ctx: Id,
val flags: List<InternalFlags>
)
}

View file

@ -792,4 +792,8 @@ class BlockMiddleware(
override suspend fun fileSpaceUsage(): FileLimits {
return middleware.fileSpaceUsage()
}
override suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload {
return middleware.setInternalFlags(command)
}
}

View file

@ -2185,6 +2185,20 @@ class Middleware @Inject constructor(
return response.event.toPayload()
}
@Throws(Exception::class)
fun setInternalFlags(
command: Command.SetInternalFlags
): Payload {
val request = Rpc.Object.SetInternalFlags.Request(
contextId = command.ctx,
internalFlags = command.flags.toMiddlewareModel()
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.setInternalFlags(request)
if (BuildConfig.DEBUG) logResponse(response)
return response.event.toPayload()
}
fun addObjectToCollection(command: Command.AddObjectToCollection): Payload {
val request = Rpc.ObjectCollection.Add.Request(
contextId = command.ctx,

View file

@ -143,6 +143,9 @@ interface MiddlewareService {
@Throws(Exception::class)
fun setObjectSource(request: Rpc.Object.SetSource.Request): Rpc.Object.SetSource.Response
@Throws(Exception::class)
fun setInternalFlags(request: Rpc.Object.SetInternalFlags.Request): Rpc.Object.SetInternalFlags.Response
//endregion
//region OBJECT'S RELATIONS command

View file

@ -1611,4 +1611,17 @@ class MiddlewareServiceImplementation @Inject constructor(
return response
}
}
override fun setInternalFlags(request: Rpc.Object.SetInternalFlags.Request): Rpc.Object.SetInternalFlags.Response {
val encoded = Service.objectSetInternalFlags(
Rpc.Object.SetInternalFlags.Request.ADAPTER.encode(request)
)
val response = Rpc.Object.SetInternalFlags.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != Rpc.Object.SetInternalFlags.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
}

View file

@ -73,6 +73,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
@ -94,6 +95,7 @@ import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.common.SupportCommand
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Interactor
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Event.ObjectTypesWidgetEvent
import com.anytypeio.anytype.presentation.editor.Editor.Restore
import com.anytypeio.anytype.presentation.editor.editor.Command
import com.anytypeio.anytype.presentation.editor.editor.Intent
@ -286,7 +288,8 @@ class EditorViewModel(
private val workspaceManager: WorkspaceManager,
private val getObjectTypes: GetObjectTypes,
private val interceptFileLimitEvents: InterceptFileLimitEvents,
private val addRelationToObject: AddRelationToObject
private val addRelationToObject: AddRelationToObject,
private val setObjectInternalFlags: SetObjectInternalFlags
) : ViewStateViewModel<ViewState>(),
PickerListener,
SupportNavigation<EventWrapper<AppNavigation.Command>>,
@ -1212,9 +1215,7 @@ class EditorViewModel(
)
viewModelScope.launch { orchestrator.stores.views.update(new) }
viewModelScope.launch { orchestrator.proxies.changes.send(update) }
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
}
fun onDescriptionBlockTextChanged(view: BlockView.Description) {
@ -1229,9 +1230,7 @@ class EditorViewModel(
)
viewModelScope.launch { orchestrator.stores.views.update(new) }
viewModelScope.launch { orchestrator.proxies.changes.send(update) }
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
}
fun onTextBlockTextChanged(view: BlockView.Text) {
@ -1257,9 +1256,7 @@ class EditorViewModel(
}
viewModelScope.launch { orchestrator.proxies.changes.send(update) }
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
}
fun onSelectionChanged(id: String, selection: IntRange) {
@ -1438,9 +1435,7 @@ class EditorViewModel(
) {
Timber.d("onEndLineEnterClicked, id:[$id] text:[$text] marks:[$marks]")
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
val target = blocks.first { it.id == id }
@ -2908,9 +2903,7 @@ class EditorViewModel(
fun onOutsideClicked() {
Timber.d("onOutsideClicked, ")
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
if (mode is EditorMode.Styling) {
onExitBlockStyleToolbarClicked()
@ -4289,11 +4282,9 @@ class EditorViewModel(
analytics = analytics,
objType = storeOfObjectTypes.get(type)
)
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
if (applyTemplate) {
proceedWithTemplateSelection(type)
proceedWithCheckingInternalFlagShouldSelectTemplate(objTypeId = type)
}
}
}
@ -5353,9 +5344,7 @@ class EditorViewModel(
Timber.d("onKeyPressedEvent, event:[$event]")
when (event) {
is KeyPressedEvent.OnTitleBlockEnterKeyEvent -> {
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
proceedWithTitleEnterClicked(
title = event.target,
text = event.text,
@ -5883,28 +5872,17 @@ class EditorViewModel(
fun onObjectTypesWidgetDoneClicked() {
Timber.d("onObjectTypesWidgetDoneClicked, ")
proceedWithHidingObjectTypeWidget()
val details = orchestrator.stores.details.current()
val wrapper = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap())
if (wrapper.internalFlags.contains(InternalFlags.ShouldSelectTemplate)) {
if (wrapper.type.isNotEmpty()) {
proceedWithTemplateSelection(typeId = wrapper.type.first())
}
}
sendHideObjectTypeWidgetEvent()
proceedWithCheckingInternalFlagShouldSelectTemplate()
}
private fun proceedWithShowingObjectTypesWidget() {
val restrictions = orchestrator.stores.objectRestrictions.current()
if (restrictions.contains(ObjectRestriction.TYPE_CHANGE)) {
Timber.d("proceedWithShowingObjectTypesWidget, type change is restricted")
return
}
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()
}
proceedWithCheckingInternalFlagShouldSelectType()
}
private fun proceedWithGettingObjectTypesForObjectTypeWidget() {
@ -5959,10 +5937,6 @@ class EditorViewModel(
)
}
private fun proceedWithHidingObjectTypeWidget() {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.ObjectTypesWidgetEvent.Hide)
}
private fun proceedWithOpeningSelectingObjectTypeScreen() {
val excludeTypes = orchestrator.stores.details.current().details[context]?.type
val command = if (isObjectTypesWidgetVisible) {
@ -5976,6 +5950,10 @@ class EditorViewModel(
}
dispatch(command)
}
private fun sendHideObjectTypeWidgetEvent() {
if (isObjectTypesWidgetVisible) controlPanelInteractor.onEvent(ObjectTypesWidgetEvent.Hide)
}
//endregion
//region OBJECT APPEARANCE SETTING
@ -6186,23 +6164,28 @@ class EditorViewModel(
viewModelScope.launch { onEvent(SelectTemplateEvent.OnSkipped) }
}
private fun proceedWithTemplateSelection(typeId: Id) {
private fun proceedWithStartTemplateEvent(objTypeId: Id) {
viewModelScope.launch {
val objType = storeOfObjectTypes.get(typeId)
val objType = storeOfObjectTypes.get(objTypeId)
if (objType != null) {
onEvent(
SelectTemplateEvent.OnStart(
ctx = context,
type = typeId,
type = objTypeId,
typeName = objType.name.orEmpty()
)
)
} else {
Timber.e("Error while getting object type from storeOfObjectTypes by id: $typeId")
Timber.e("Error while getting object type from storeOfObjectTypes by id: $objTypeId")
}
}
}
private fun getObjectTypeFromDetails(): Id? {
val details = orchestrator.stores.details.current()
val wrapper = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap())
return wrapper.getProperType()
}
//endregion
//region SIMPLE TABLES
@ -6892,6 +6875,84 @@ class EditorViewModel(
}
}
//endregion
//region INTERNAL FLAGS
private fun proceedWithCheckingInternalFlagShouldSelectType() {
val internalFlags = getInternalFlagsFromDetails()
if (internalFlags.contains(InternalFlags.ShouldSelectType)) {
//We use this flag to show object type widget and then we don't need it anymore
proceedWithGettingObjectTypesForObjectTypeWidget()
proceedWithOptOutTypeInternalFlag()
} else {
Timber.d("Object doesn't have internal flag: ShouldSelectType")
}
}
private fun proceedWithCheckingInternalFlagShouldSelectTemplate(objTypeId: Id? = null) {
val internalFlags = getInternalFlagsFromDetails()
if (internalFlags.contains(InternalFlags.ShouldSelectTemplate)) {
//We use this flag to show template widget and then we don't need it anymore
val properObjTypeId = objTypeId ?: getObjectTypeFromDetails() ?: return
proceedWithStartTemplateEvent(objTypeId = properObjTypeId)
proceedWithOptOutTemplateInternalFlag()
} else {
Timber.d("Object doesn't have internal flag: ShouldSelectTemplate")
}
}
private fun proceedWithOptOutTypeInternalFlag() {
val internalFlags = getInternalFlagsFromDetails()
if (!internalFlags.contains(InternalFlags.ShouldSelectType)) return
val flagsWithoutType = filterOutInternalFlags(
flags = internalFlags,
out = InternalFlags.ShouldSelectType
)
updateFlagsAndProceed(
flags = flagsWithoutType,
action = this::sendHideObjectTypeWidgetEvent
)
}
private fun proceedWithOptOutTemplateInternalFlag() {
val internalFlags = getInternalFlagsFromDetails()
if (!internalFlags.contains(InternalFlags.ShouldSelectTemplate)) return
val flagsWithoutTemplate = filterOutInternalFlags(
flags = internalFlags,
out = InternalFlags.ShouldSelectTemplate
)
updateFlagsAndProceed(flags = flagsWithoutTemplate)
}
private fun getInternalFlagsFromDetails(): List<InternalFlags> {
val details = orchestrator.stores.details.current()
val obj = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap())
return obj.internalFlags
}
private fun filterOutInternalFlags(flags: List<InternalFlags>, out: InternalFlags): List<InternalFlags> {
return flags.filter { it != out }
}
private fun updateFlagsAndProceed(flags: List<InternalFlags>, action: () -> Unit = {}) {
viewModelScope.launch {
val params = SetObjectInternalFlags.Params(
ctx = context,
flags = flags
)
setObjectInternalFlags.async(params).fold(
onSuccess = {
dispatcher.send(it)
Timber.d("Internal flags updated")
action.invoke()
},
onFailure = {
Timber.e(it, "Error while updating internal flags")
action.invoke()
}
)
}
}
//endregion
}
private const val NO_POSITION = -1

View file

@ -18,6 +18,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
@ -79,7 +80,8 @@ open class EditorViewModelFactory(
private val getObjectTypes: GetObjectTypes,
private val objectToCollection: ConvertObjectToCollection,
private val interceptFileLimitEvents: InterceptFileLimitEvents,
private val addRelationToObject: AddRelationToObject
private val addRelationToObject: AddRelationToObject,
private val setObjectInternalFlags: SetObjectInternalFlags
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -120,7 +122,8 @@ open class EditorViewModelFactory(
workspaceManager = workspaceManager,
getObjectTypes = getObjectTypes,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
) as T
}
}

View file

@ -127,6 +127,10 @@ class TemplateSelectViewModel(
}
}
fun onCancelButtonClicked() {
isDismissed.value = true
}
class Factory @Inject constructor(
private val applyTemplate: ApplyTemplate,
private val getTemplates: GetTemplates,
@ -149,7 +153,6 @@ class TemplateSelectViewModel(
) : ViewState()
object Init : ViewState()
object ErrorGettingType : ViewState()
}
}

View file

@ -61,6 +61,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultStoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
@ -352,6 +353,7 @@ open class EditorViewModelTest {
private lateinit var objectToSet: ConvertObjectToSet
private lateinit var clearBlockContent: ClearBlockContent
private lateinit var clearBlockStyle: ClearBlockStyle
private lateinit var setObjectInternalFlags: SetObjectInternalFlags
val root = MockDataFactory.randomUuid()
@ -3812,6 +3814,7 @@ open class EditorViewModelTest {
clearBlockContent = ClearBlockContent(repo)
clearBlockStyle = ClearBlockStyle(repo)
interceptFileLimitEvents = InterceptFileLimitEvents(fileLimitsEventChannel, dispatchers)
setObjectInternalFlags = SetObjectInternalFlags(repo, dispatchers)
workspaceManager = WorkspaceManager.DefaultWorkspaceManager()
runBlocking {
@ -3904,7 +3907,8 @@ open class EditorViewModelTest {
workspaceManager = workspaceManager,
getObjectTypes = getObjectTypes,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)
}

View file

@ -10,6 +10,7 @@ import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubSmartBlock
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule
import kotlin.test.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -19,6 +20,10 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyBlocking
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.verifyNoMoreInteractions
@ExperimentalCoroutinesApi
class EditorInternalFlagsTest : EditorPresentationTestSetup() {
@ -99,4 +104,203 @@ class EditorInternalFlagsTest : EditorPresentationTestSetup() {
assertEquals(expected = expectedFlags, actual = actualFlags)
}
@Test
fun `should remove type flag on show object event with type flag in details`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectTemplate.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble(),
InternalFlags.ShouldSelectType.code.toDouble(),
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
verifyBlocking(setObjectInternalFlags, times(1)) {
async(
params = SetObjectInternalFlags.Params(
ctx = root,
flags = listOf(
InternalFlags.ShouldSelectTemplate,
InternalFlags.ShouldEmptyDelete
)
)
)
}
coroutineTestRule.advanceTime(100)
}
@Test
fun `should not remove type flag on show object event without type flag in details`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectTemplate.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble()
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
verifyNoInteractions(setObjectInternalFlags)
coroutineTestRule.advanceTime(100)
}
@Test
fun `should remove template flag on start template selection widget`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectTemplate.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble(),
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
vm.onObjectTypesWidgetDoneClicked()
advanceUntilIdle()
verifyBlocking(setObjectInternalFlags, times(1)) {
async(
params = SetObjectInternalFlags.Params(
ctx = root,
flags = listOf(
InternalFlags.ShouldEmptyDelete
)
)
)
}
coroutineTestRule.advanceTime(100)
}
@Test
fun `should not remove template flag on start template selection widget when flag isn't present`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectType.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble(),
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
vm.onObjectTypesWidgetDoneClicked()
advanceUntilIdle()
verifyBlocking(setObjectInternalFlags, times(1)) {
async(
params = SetObjectInternalFlags.Params(
ctx = root,
flags = listOf(
InternalFlags.ShouldEmptyDelete
)
)
)
}
advanceUntilIdle()
verifyNoMoreInteractions(setObjectInternalFlags)
coroutineTestRule.advanceTime(100)
}
}

View file

@ -50,6 +50,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultStoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
@ -346,6 +347,9 @@ open class EditorPresentationTestSetup {
lateinit var fileLimitsEventChannel: FileLimitsEventChannel
lateinit var interceptFileLimitEvents: InterceptFileLimitEvents
@Mock
lateinit var setObjectInternalFlags: SetObjectInternalFlags
open fun buildViewModel(urlBuilder: UrlBuilder = builder): EditorViewModel {
val storage = Editor.Storage()
@ -467,7 +471,8 @@ open class EditorPresentationTestSetup {
workspaceManager = workspaceManager,
getObjectTypes = getObjectTypes,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)
}
@ -746,4 +751,16 @@ open class EditorPresentationTestSetup {
} doReturn Resultat.success(types)
}
}
fun stubFileLimitEvents() {
interceptFileLimitEvents.stub {
onBlocking { run(Unit) } doReturn emptyFlow()
}
}
fun stubSetInternalFlags() {
setObjectInternalFlags.stub {
onBlocking { async(any()) } doReturn Resultat.success(Payload(root, emptyList()))
}
}
}