diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt index 77bc63ce86..fdd7f1fabc 100644 --- a/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt +++ b/app/src/androidTest/java/com/anytypeio/anytype/features/sets/dv/TestObjectSetSetup.kt @@ -209,10 +209,8 @@ abstract class TestObjectSetSetup { dispatchers = dispatchers ) createDataViewObject = CreateDataViewObject( - getTemplates = getTemplates, repo = repo, storeOfRelations = storeOfRelations, - getDefaultPageType = getDefaultPageType, dispatchers = dispatchers ) setObjectDetails = UpdateDetail(repo) diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt index dbac745d74..6b06ed9e56 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/ObjectSetDI.kt @@ -331,13 +331,9 @@ object ObjectSetModule { fun provideCreateDataViewRecordUseCase( repo: BlockRepository, storeOfRelations: StoreOfRelations, - getDefaultPageType: GetDefaultPageType, - getTemplates: GetTemplates, dispatchers: AppCoroutineDispatchers ): CreateDataViewObject = CreateDataViewObject( repo = repo, - getDefaultPageType = getDefaultPageType, - getTemplates = getTemplates, storeOfRelations = storeOfRelations, dispatchers = dispatchers ) diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt index 419fc171c1..14a13f4933 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt @@ -287,7 +287,8 @@ data class Block( val hideIcon: Boolean = false, val coverFit: Boolean = false, val coverRelationKey: String? = null, - val defaultTemplateId: Id? = null, + val defaultTemplate: Id? = null, + val defaultObjectType: Id? = null, ) { enum class Type(val formattedName: String) { diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/dataview/interactor/CreateDataViewObject.kt b/domain/src/main/java/com/anytypeio/anytype/domain/dataview/interactor/CreateDataViewObject.kt index b30ab4227b..ad54589779 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/dataview/interactor/CreateDataViewObject.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/dataview/interactor/CreateDataViewObject.kt @@ -4,9 +4,7 @@ import com.anytypeio.anytype.core_models.Command import com.anytypeio.anytype.core_models.DVFilter import com.anytypeio.anytype.core_models.DVFilterCondition 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.ObjectTypeIds import com.anytypeio.anytype.core_models.Relation import com.anytypeio.anytype.core_models.RelationFormat import com.anytypeio.anytype.core_models.Relations @@ -14,17 +12,13 @@ import com.anytypeio.anytype.core_models.Struct import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.base.ResultInteractor import com.anytypeio.anytype.domain.block.repo.BlockRepository -import com.anytypeio.anytype.domain.launch.GetDefaultPageType import com.anytypeio.anytype.domain.objects.StoreOfRelations -import com.anytypeio.anytype.domain.templates.GetTemplates /** * Use-case for creating a new record inside data view's database. */ class CreateDataViewObject( private val repo: BlockRepository, - private val getTemplates: GetTemplates, - private val getDefaultPageType: GetDefaultPageType, private val storeOfRelations: StoreOfRelations, dispatchers: AppCoroutineDispatchers ) : ResultInteractor(dispatchers.io) { @@ -47,37 +41,35 @@ class CreateDataViewObject( ) } is Params.SetByRelation -> { - val type = resolveDefaultObjectType() val command = Command.CreateObject( template = params.template, prefilled = resolveSetByRelationPrefilledObjectData( filters = params.filters, relations = params.relations, - type = type + type = params.type ), internalFlags = listOf() ) val result = repo.createObject(command) Result( objectId = result.id, - objectType = type + objectType = params.type ) } is Params.Collection -> { - val type = resolveDefaultObjectType() val command = Command.CreateObject( template = params.templateId, prefilled = resolveSetByRelationPrefilledObjectData( filters = emptyList(), relations = emptyList(), - type = type + type = params.type ), internalFlags = listOf() ) val result = repo.createObject(command) Result( objectId = result.id, - objectType = type + objectType = params.type ) } } @@ -99,15 +91,6 @@ class CreateDataViewObject( put(Relations.TYPE, type) } - private suspend fun resolveTemplateForNewObject(type: Id): Id? { - val templates = try { - getTemplates.run(GetTemplates.Params(type)) - } catch (e: Exception) { - emptyList() - } - return templates.singleOrNull()?.id - } - private suspend fun resolveSetByRelationPrefilledObjectData( filters: List, relations: List, @@ -141,14 +124,6 @@ class CreateDataViewObject( emptyMap() } - private suspend fun resolveDefaultObjectType(): Id { - return try { - getDefaultPageType.run(Unit).type ?: ObjectTypeIds.NOTE - } catch (e: Exception) { - ObjectTypeIds.NOTE - } - } - private fun resolveDefaultValueByFormat(format: RelationFormat): Any? { when (format) { Relation.Format.LONG_TEXT, @@ -181,10 +156,12 @@ class CreateDataViewObject( data class SetByRelation( val filters: List, val relations: List, - val template: Id? + val template: Id?, + val type: Id? ) : Params() data class Collection( + val type: Id?, val templateId: Id? ) : Params() } diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt index 9f1a88a122..9cb9df474c 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt @@ -24,6 +24,7 @@ import com.anytypeio.anytype.core_models.FileLimits import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectOrder import com.anytypeio.anytype.core_models.ObjectType +import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.core_models.ObjectView import com.anytypeio.anytype.core_models.Payload import com.anytypeio.anytype.core_models.Relation @@ -503,7 +504,8 @@ fun MDVView.toCoreModels(): DVViewer = DVViewer( hideIcon = hideIcon, coverFit = coverFit, coverRelationKey = coverRelationKey.ifEmpty { null }, - defaultTemplateId = defaultTemplateId.ifEmpty { null } + defaultTemplate = defaultTemplateId.ifEmpty { null }, + defaultObjectType = defaultObjectTypeId.ifEmpty { ObjectTypeIds.PAGE } ) fun MDVRelation.toCoreModels(): DVViewerRelation = DVViewerRelation( diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToMiddlewareModelMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToMiddlewareModelMappers.kt index eea99e0625..fa7fe315fa 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToMiddlewareModelMappers.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToMiddlewareModelMappers.kt @@ -329,7 +329,8 @@ fun Block.Content.DataView.Viewer.toMiddlewareModel(): MDVView = Block.Content.DataView.Viewer.Size.MEDIUM -> MDVViewCardSize.Medium Block.Content.DataView.Viewer.Size.LARGE -> MDVViewCardSize.Large }, - defaultTemplateId = defaultTemplateId.orEmpty() + defaultTemplateId = defaultTemplate.orEmpty(), + defaultObjectTypeId = defaultObjectType.orEmpty(), ) fun Block.Content.DataView.Viewer.Type.toMiddlewareModel(): MDVViewType = when (this) { diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt index 0b9be0cf6d..7041108025 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt @@ -7,7 +7,6 @@ import com.anytypeio.anytype.core_models.DVRecord import com.anytypeio.anytype.core_models.DVSort import com.anytypeio.anytype.core_models.DVViewer import com.anytypeio.anytype.core_models.DVViewerRelation -import com.anytypeio.anytype.core_models.DVViewerType import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVFilterUpdate import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVSortUpdate import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerFields @@ -44,6 +43,8 @@ import com.anytypeio.anytype.presentation.sets.model.ObjectView import com.anytypeio.anytype.presentation.sets.model.SimpleRelationView import com.anytypeio.anytype.presentation.sets.model.Viewer import com.anytypeio.anytype.presentation.sets.state.ObjectState +import com.anytypeio.anytype.presentation.sets.state.ObjectState.Companion.VIEW_DEFAULT_OBJECT_TYPE +import com.anytypeio.anytype.presentation.sets.state.ObjectState.Companion.VIEW_TYPE_UNSUPPORTED import com.anytypeio.anytype.presentation.sets.viewer.ViewerView import com.anytypeio.anytype.presentation.templates.TemplateView @@ -380,23 +381,9 @@ fun ObjectState.DataView.filterOutDeletedAndMissingObjects(query: List): Lis return query.filter(::isValidObject) } -suspend fun ObjectState.DataView.Set.isTemplatesAllowed( - setOfValue: List, - storeOfObjectTypes: StoreOfObjectTypes, - getDefaultPageType: GetDefaultPageType -): Boolean { +fun ObjectState.DataView.Set.isSetByRelation(setOfValue: List): Boolean { val objectDetails = details[setOfValue.first()]?.map.orEmpty() - return when (objectDetails.type) { - ObjectTypeIds.OBJECT_TYPE -> { - val objectWrapper = ObjectWrapper.Type(objectDetails) - objectWrapper.isTemplatesAllowed() - } - ObjectTypeIds.RELATION -> { - //We have set of relations, need to check default object type - storeOfObjectTypes.isTemplatesAllowedForDefaultType(getDefaultPageType) - } - else -> false - } + return objectDetails.type == ObjectTypeIds.RELATION } suspend fun StoreOfObjectTypes.isTemplatesAllowedForDefaultType(getDefaultPageType: GetDefaultPageType): Boolean { @@ -424,7 +411,7 @@ fun DVViewer.updateFields(fields: DVViewerFields?): DVViewer { hideIcon = fields.hideIcon, cardSize = fields.cardSize, coverFit = fields.coverFit, - defaultTemplateId = fields.defaultTemplateId + defaultTemplate = fields.defaultTemplateId ) } @@ -469,17 +456,61 @@ fun ObjectWrapper.Type.toTemplateViewBlank(): TemplateView.Blank { ) } -fun List.toView(session: ObjectSetSession): List { - return mapIndexed { index, viewer -> +fun ObjectState.DataView.toViewersView(ctx: Id, session: ObjectSetSession): List { + val viewers = dataViewContent.viewers + return when (this) { + is ObjectState.DataView.Collection -> mapViewers( + defaultObjectType = { it.defaultObjectType }, + viewers = viewers, + session = session + ) + is ObjectState.DataView.Set -> { + val setOfValue = getSetOfValue(ctx) + if (isSetByRelation(setOfValue = setOfValue)) { + mapViewers( + defaultObjectType = { it.defaultObjectType }, + viewers = viewers, + session = session + ) + } else { + mapViewers( + defaultObjectType = { setOfValue.firstOrNull() }, + viewers = viewers, + session = session + ) + } + } + } +} + +private fun mapViewers( + defaultObjectType: (DVViewer) -> Id?, + viewers: List, + session: ObjectSetSession +): List { + return viewers.mapIndexed { index, viewer -> ViewerView( id = viewer.id, name = viewer.name, type = viewer.type, - isActive = if (session.currentViewerId.value != null) - viewer.id == session.currentViewerId.value - else - index == 0, - isUnsupported = viewer.type == DVViewerType.BOARD + isActive = isActiveViewer(index, viewer, session), + isUnsupported = viewer.type == VIEW_TYPE_UNSUPPORTED, + defaultObjectType = defaultObjectType.invoke(viewer) ) } +} + +private fun isActiveViewer(index: Int, viewer: DVViewer, session: ObjectSetSession): Boolean { + return if (session.currentViewerId.value != null) { + viewer.id == session.currentViewerId.value + } else { + index == 0 + } +} + +suspend fun List.isActiveWithTemplates(storeOfObjectTypes: StoreOfObjectTypes): Boolean { + val activeViewer = firstOrNull { it.isActive } + val viewerDefaultObjectTypeId = activeViewer?.defaultObjectType ?: return false + val viewerDefaultObjectType = storeOfObjectTypes.get(viewerDefaultObjectTypeId) ?: return false + return viewerDefaultObjectType.isTemplatesAllowed() } \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt index 53758282c9..d4f3b669df 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt @@ -67,6 +67,7 @@ import com.anytypeio.anytype.presentation.relations.render import com.anytypeio.anytype.presentation.sets.model.CellView import com.anytypeio.anytype.presentation.sets.model.Viewer import com.anytypeio.anytype.presentation.sets.state.ObjectState +import com.anytypeio.anytype.presentation.sets.state.ObjectState.Companion.VIEW_DEFAULT_OBJECT_TYPE import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription import com.anytypeio.anytype.presentation.sets.viewer.ViewerDelegate @@ -199,12 +200,12 @@ class ObjectSetViewModel( urlBuilder = urlBuilder, coverImageHashProvider = coverImageHashProvider ) - gettingTemplatesForDataViewState(state) } } subscribeToObjectState() subscribeToDataViewViewer() + subscribeToViewerTypeTemplates() viewModelScope.launch { dispatcher.flow().collect { defaultPayloadConsumer(it) } @@ -444,10 +445,9 @@ class ObjectSetViewModel( combine( database.index, stateReducer.state, - session.currentViewerId, - _templateViews - ) { dataViewState, objectState, currentViewId, templates -> - processViewState(dataViewState, objectState, currentViewId, templates) + session.currentViewerId + ) { dataViewState, objectState, currentViewId -> + processViewState(dataViewState, objectState, currentViewId) }.distinctUntilChanged().collect { viewState -> Timber.d("subscribeToDataViewViewer, newViewerState:[$viewState]") _currentViewer.value = viewState @@ -458,20 +458,18 @@ class ObjectSetViewModel( private suspend fun processViewState( dataViewState: DataViewState, objectState: ObjectState, - currentViewId: String?, - templates: List + currentViewId: String? ): DataViewViewState { return when (objectState) { is ObjectState.DataView.Collection -> processCollectionState( dataViewState = dataViewState, objectState = objectState, - currentViewId = currentViewId, - templates = templates + currentViewId = currentViewId ) is ObjectState.DataView.Set -> processSetState( dataViewState = dataViewState, objectState = objectState, - currentViewId = currentViewId, + currentViewId = currentViewId ) ObjectState.Init -> DataViewViewState.Init ObjectState.ErrorLayout -> DataViewViewState.Error(msg = "Wrong layout, couldn't open object") @@ -482,7 +480,6 @@ class ObjectSetViewModel( dataViewState: DataViewState, objectState: ObjectState.DataView.Collection, currentViewId: String?, - templates: List ): DataViewViewState { if (!objectState.isInitialized) return DataViewViewState.Init @@ -498,7 +495,7 @@ class ObjectSetViewModel( } } is DataViewState.Loaded -> { - _dvViews.value = objectState.viewers.toView(session) + _dvViews.value = objectState.dataViewState()?.toViewersView(context, session) ?: emptyList() val relations = objectState.dataViewContent.relationLinks.mapNotNull { storeOfRelations.getByKey(it.key) } @@ -507,23 +504,17 @@ class ObjectSetViewModel( when { viewer == null -> DataViewViewState.Collection.NoView viewer.isEmpty() -> { - val isTemplatesPresent = templates.isNotEmpty() && - storeOfObjectTypes.isTemplatesAllowedForDefaultType( - getDefaultPageType = getDefaultPageType - ) + val hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) DataViewViewState.Collection.NoItems( title = viewer.title, - hasTemplates = isTemplatesPresent + hasTemplates = hasTemplates ) } else -> { - val isTemplatesPresent = templates.isNotEmpty() && - storeOfObjectTypes.isTemplatesAllowedForDefaultType( - getDefaultPageType = getDefaultPageType - ) + val hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) DataViewViewState.Collection.Default( viewer = viewer, - hasTemplates = isTemplatesPresent + hasTemplates = hasTemplates ) } } @@ -552,7 +543,7 @@ class ObjectSetViewModel( } } is DataViewState.Loaded -> { - _dvViews.value = objectState.viewers.toView(session) + _dvViews.value = objectState.dataViewState()?.toViewersView(context, session) ?: emptyList() val relations = objectState.dataViewContent.relationLinks.mapNotNull { storeOfRelations.getByKey(it.key) } @@ -569,25 +560,15 @@ class ObjectSetViewModel( query.isEmpty() || setOfValue.isEmpty() -> DataViewViewState.Set.NoQuery render == null -> DataViewViewState.Set.NoView render.isEmpty() -> { - val isTemplatesAllowed = objectState.isTemplatesAllowed( - setOfValue, - storeOfObjectTypes, - getDefaultPageType - ) DataViewViewState.Set.NoItems( title = render.title, - hasTemplates = isTemplatesAllowed + hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) ) } else -> { - val isTemplatesAllowed = objectState.isTemplatesAllowed( - setOfValue, - storeOfObjectTypes, - getDefaultPageType - ) DataViewViewState.Set.Default( viewer = render, - hasTemplates = isTemplatesAllowed + hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) ) } } @@ -963,7 +944,8 @@ class ObjectSetViewModel( CreateDataViewObject.Params.SetByRelation( filters = viewer.filters, relations = setObject.setOf, - template = templateId + template = templateId, + type = viewer.defaultObjectType ) ) } @@ -985,8 +967,11 @@ class ObjectSetViewModel( } private fun proceedWithAddingObjectToCollection(templateId: Id? = null) { + val state = stateReducer.state.value.dataViewState() ?: return + val viewer = state.viewerById(session.currentViewerId.value) ?: return val createObjectParams = CreateDataViewObject.Params.Collection( - templateId = templateId + templateId = templateId, + type = viewer.defaultObjectType ) proceedWithCreatingDataViewObject(createObjectParams) { result -> val params = AddObjectToCollection.Params( @@ -1506,70 +1491,74 @@ class ObjectSetViewModel( } // region TEMPLATES - private suspend fun gettingTemplatesForDataViewState(state: ObjectState.DataView) { - when (state) { - is ObjectState.DataView.Collection -> { - subscribeForTypeTemplates(typeId = null) - } - is ObjectState.DataView.Set -> { - val sourceId = proceedWithGettingSetSourceId(state) - val sourceMap = state.details[sourceId] ?: return - when (sourceMap.type.firstOrNull()) { - ObjectTypeIds.RELATION -> subscribeForTypeTemplates(typeId = null) - ObjectTypeIds.OBJECT_TYPE -> subscribeForTypeTemplates(typeId = sourceId) - else -> { Timber.d("Ignoring type of source") } + private fun subscribeToViewerTypeTemplates() { + viewModelScope.launch { + combine( + stateReducer.state.filterIsInstance(), + session.currentViewerId + ) { state, currentViewId -> + + val viewer = state.dataViewState()?.viewerById(currentViewId) + val viewerDefObjType = fetchViewerDefaultObjectType(viewer) + + if (viewer != null && viewerDefObjType?.isTemplatesAllowed() == true) { + fetchAndProcessTemplates(viewerDefObjType, viewer) + } else { + Timber.d("Templates are not allowed for type:[${viewerDefObjType?.id}]") + _dvViews.value = emptyList() } - } + }.collect() } } - private suspend fun subscribeForTypeTemplates(typeId: Id?) { - val state = stateReducer.state.value.dataViewState() ?: return - val viewer = state.viewerById(session.currentViewerId.value) ?: return - val objectType = resolveObjectType(typeId) - if (objectType?.isTemplatesAllowed() == true) { - viewModelScope.launch { - templatesContainer.subscribe(objectType.id) - .catch { error -> - Timber.e(error, "Error while getting templates for type ${objectType.id}") - toast("Error while getting templates for type ${objectType.id}") - _templateViews.value = emptyList() - } - .map { results -> - Timber.d("subscribeForTypeTemplates, new templates size:[${results.size}]") - val blankTemplate = listOf(objectType.toTemplateViewBlank()) - blankTemplate + results - .map { objTemplate -> - objTemplate.toTemplateView( - typeId = objectType.id, - urlBuilder = urlBuilder, - coverImageHashProvider = coverImageHashProvider, - objectTypeDefaultTemplate = objectType.defaultTemplateId, - viewerDefaultTemplate = viewer.defaultTemplateId - ) - } + listOf(TemplateView.New(objectType.id)) - }.collectLatest { - _templateViews.value = it - } - } - } else { - Timber.d("Templates are not allowed for type:[${objectType?.id}]") - } + private suspend fun fetchViewerDefaultObjectType(viewer: DVViewer?): ObjectWrapper.Type? { + val viewerDefaultObjectTypeId = viewer?.defaultObjectType ?: VIEW_DEFAULT_OBJECT_TYPE + return storeOfObjectTypes.get(viewerDefaultObjectTypeId) } - private suspend fun resolveObjectType(type: Id?): ObjectWrapper.Type? { - return if (type == null) { - val defaultObjectType = getDefaultPageType.run(Unit).type ?: return null - storeOfObjectTypes.get(defaultObjectType) - } else { - storeOfObjectTypes.get(type) - } + private suspend fun fetchAndProcessTemplates(viewerDefObjType: ObjectWrapper.Type, viewer: DVViewer) { + Timber.d("Fetching templates for type ${viewerDefObjType.id}") + + templatesContainer.subscribe(viewerDefObjType.id) + .catch { + handleTemplateFetchingError(it, viewerDefObjType.id) + } + .map { results -> + processTemplates(results, viewerDefObjType, viewer) + } + .collectLatest { templates -> + _templateViews.value = templates + } + } + + private fun handleTemplateFetchingError(exception: Throwable, typeId: String) { + Timber.e(exception, "Error while getting templates for type $typeId") + toast("Error while getting templates for type $typeId") + _dvViews.value = emptyList() + } + + private fun processTemplates( + results: List, + viewerDefObjType: ObjectWrapper.Type, + viewer: DVViewer + ): List { + val blankTemplate = listOf(viewerDefObjType.toTemplateViewBlank()) + return blankTemplate + results.map { objTemplate -> + objTemplate.toTemplateView( + typeId = viewerDefObjType.id, + urlBuilder = urlBuilder, + coverImageHashProvider = coverImageHashProvider, + objectTypeDefaultTemplate = viewerDefObjType.defaultTemplateId, + viewerDefaultTemplate = viewer.defaultTemplate + ) + } + listOf(TemplateView.New(viewerDefObjType.id)) } fun onTemplateItemClicked(item: TemplateView) { val state = templatesWidgetState.value if (state.isMoreMenuVisible) { - templatesWidgetState.value = state.copy(isMoreMenuVisible = false, moreMenuTemplate = null) + templatesWidgetState.value = + state.copy(isMoreMenuVisible = false, moreMenuTemplate = null) return } when(item) { @@ -1638,21 +1627,6 @@ class ObjectSetViewModel( } } - private fun proceedWithGettingSetSourceId(currentState: ObjectState.DataView.Set): Id? { - if (isRestrictionPresent(DataViewRestriction.CREATE_OBJECT) || !currentState.isInitialized) { - toast(NOT_ALLOWED) - return null - } - - val setObject = ObjectWrapper.Basic(currentState.details[context]?.map ?: emptyMap()) - val sourceId = setObject.setOf.singleOrNull() - if (sourceId == null) { - Timber.e("Unable to define a source for a new object.") - toast("Unable to define a source for a new object.") - } - return sourceId - } - fun onEditTemplateButtonClicked() { templatesWidgetState.value = templatesWidgetState.value.copy(isEditing = true) } @@ -1713,7 +1687,7 @@ class ObjectSetViewModel( val params = UpdateDataViewViewer.Params.Template( context = context, target = state.dataViewBlock.id, - viewer = viewer.copy(defaultTemplateId = template.id) + viewer = viewer.copy(defaultTemplate = template.id) ) viewModelScope.launch { updateDataViewViewer(params).proceed( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt index bc5911c552..3e0944636b 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt @@ -3,7 +3,9 @@ package com.anytypeio.anytype.presentation.sets.state import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.DV import com.anytypeio.anytype.core_models.DVViewer +import com.anytypeio.anytype.core_models.DVViewerType import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.core_models.RelationLink import com.anytypeio.anytype.core_models.restrictions.DataViewRestrictions import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction @@ -66,4 +68,9 @@ sealed class ObjectState { override val isInitialized: Boolean get() = false } + + companion object { + const val VIEW_DEFAULT_OBJECT_TYPE = ObjectTypeIds.PAGE + val VIEW_TYPE_UNSUPPORTED = DVViewerType.BOARD + } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/viewer/ViewerView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/viewer/ViewerView.kt index ab86f10d99..90d958bf3d 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/viewer/ViewerView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/viewer/ViewerView.kt @@ -9,7 +9,8 @@ data class ViewerView( val type: DVViewerType, val isActive: Boolean, val showActionMenu: Boolean = false, - val isUnsupported: Boolean = false + val isUnsupported: Boolean = false, + val defaultObjectType: Id? ) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/ObjectTypeTemplatesContainer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/ObjectTypeTemplatesContainer.kt index dd4a66154f..15e0d71bce 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/ObjectTypeTemplatesContainer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/ObjectTypeTemplatesContainer.kt @@ -10,9 +10,7 @@ import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.domain.library.StoreSearchParams import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer -import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer.Companion.SUBSCRIPTION_TEMPLATES import com.anytypeio.anytype.domain.workspace.WorkspaceManager -import javax.inject.Named import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionTemplatesDelegateTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionTemplatesDelegateTest.kt index 726286c179..e4e59a0e23 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionTemplatesDelegateTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionTemplatesDelegateTest.kt @@ -52,7 +52,7 @@ class CollectionTemplatesDelegateTest: ObjectSetViewModelTestSetup() { stubWorkspaceManager(mockCollection.workspaceId) stubInterceptEvents() stubInterceptThreadStatus() - stubStoreOfObjectTypes(defaultTypeMap) + stubStoreOfObjectTypes(defaultType, defaultTypeMap) stubGetDefaultPageType(type = defaultType, name = defaultTypeName) stubTemplatesContainer(type = defaultType) @@ -109,7 +109,7 @@ class CollectionTemplatesDelegateTest: ObjectSetViewModelTestSetup() { stubWorkspaceManager(mockCollection.workspaceId) stubInterceptEvents() stubInterceptThreadStatus() - stubStoreOfObjectTypes(defaultTypeMap) + stubStoreOfObjectTypes(defaultType, defaultTypeMap) stubGetDefaultPageType(type = defaultType, name = defaultTypeName) val details = Block.Details( diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockSet.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockSet.kt index b337200356..13f6a073dd 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockSet.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockSet.kt @@ -5,6 +5,7 @@ import com.anytypeio.anytype.core_models.DVFilter import com.anytypeio.anytype.core_models.DVViewerType import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectTypeIds +import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.Relation import com.anytypeio.anytype.core_models.RelationFormat import com.anytypeio.anytype.core_models.Relations @@ -35,7 +36,6 @@ class MockSet(context: String, val setOfValue: String = "setOf-${RandomString.ma val workspaceId = "workspace-${RandomString.make()}" val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(context) val setOf get() = setOfValue - val setOfNote = ObjectTypeIds.NOTE // RELATION OBJECTS val relationObject1 = StubRelationObject( @@ -69,7 +69,7 @@ class MockSet(context: String, val setOfValue: String = "setOf-${RandomString.ma StubDataViewViewRelation(key = relationObject1.key, isVisible = true) private val dvViewerRelation2 = StubDataViewViewRelation(key = relationObject2.key, isVisible = true) - private val dvViewerRelation3 = + val dvViewerRelation3 = StubDataViewViewRelation(key = relationObject3.key, isVisible = true) private val dvViewerRelation4 = StubDataViewViewRelation(key = relationObject4.key, isVisible = true) @@ -77,11 +77,11 @@ class MockSet(context: String, val setOfValue: String = "setOf-${RandomString.ma StubDataViewViewRelation(key = relationObject5.key, isVisible = true) // RELATION LINKS - private val relationLink1 = StubRelationLink(relationObject1.key) - private val relationLink2 = StubRelationLink(relationObject2.key) - private val relationLink3 = StubRelationLink(relationObject3.key) - private val relationLink4 = StubRelationLink(relationObject4.key) - private val relationLink5 = StubRelationLink(relationObject5.key) + val relationLink1 = StubRelationLink(relationObject1.key) + val relationLink2 = StubRelationLink(relationObject2.key) + val relationLink3 = StubRelationLink(relationObject3.key) + val relationLink4 = StubRelationLink(relationObject4.key) + val relationLink5 = StubRelationLink(relationObject5.key) // SEARCH OBJECTS COMMAND, RELATION KEYS val dvKeys = listOf( @@ -111,7 +111,7 @@ class MockSet(context: String, val setOfValue: String = "setOf-${RandomString.ma ) // VIEWS - private val viewerList = + val viewerList = StubDataViewView( id = "dvViewerList-${RandomString.make()}", viewerRelations = listOf( @@ -136,7 +136,7 @@ class MockSet(context: String, val setOfValue: String = "setOf-${RandomString.ma type = DVViewerType.GRID, filters = filters ) - private val viewerGallery = + val viewerGallery = StubDataViewView( id = "dvViewerGallery-${RandomString.make()}", viewerRelations = listOf( @@ -217,4 +217,23 @@ class MockSet(context: String, val setOfValue: String = "setOf-${RandomString.ma ) ) ) + + fun detailsSetByRelation(relationSetBy: ObjectWrapper.Relation) = Block.Details( + details = mapOf( + root to Block.Fields( + mapOf( + Relations.ID to root, + Relations.LAYOUT to ObjectType.Layout.SET.code.toDouble(), + Relations.SET_OF to relationSetBy.key + ) + ), + relationSetBy.key to Block.Fields( + mapOf( + Relations.ID to relationSetBy.id, + Relations.RELATION_KEY to relationSetBy.key, + Relations.TYPE to ObjectTypeIds.RELATION + ) + ) + ) + ) } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt index 0140011ef6..96fd11396d 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt @@ -2,6 +2,8 @@ package com.anytypeio.anytype.presentation.collections import app.cash.turbine.testIn import com.anytypeio.anytype.core_models.ObjectTypeIds +import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.core_models.StubRelationObject import com.anytypeio.anytype.domain.base.Resultat import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject import com.anytypeio.anytype.presentation.sets.ObjectSetCommand @@ -162,7 +164,13 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { @Test fun `Should create and open Object when clicking on New button in Set by Relations`() = runTest { - mockObjectSet = MockSet(context = root, setOfValue = ObjectTypeIds.NOTE) + val setByRelationValue = "setByRelation-${RandomString.make()}" + mockObjectSet = MockSet(context = root, setOfValue = setByRelationValue) + val relationSetBy = StubRelationObject( + key = setByRelationValue, + isReadOnlyValue = false, + format = Relation.Format.LONG_TEXT + ) // SETUP stubWorkspaceManager(mockObjectSet.workspaceId) @@ -170,14 +178,17 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { stubInterceptThreadStatus() stubOpenObject( doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView), - details = mockObjectSet.detailsSetByRelation + details = mockObjectSet.detailsSetByRelation(relationSetBy) ) + //stubStoreOfRelations(mockObjectSet) + storeOfRelations.merge(listOf(relationSetBy)) + stubSubscriptionResults( subscription = mockObjectSet.subscriptionId, workspace = mockObjectSet.workspaceId, storeOfRelations = storeOfRelations, keys = mockObjectSet.dvKeys, - sources = listOf(mockObjectSet.setOf), + sources = listOf(setByRelationValue), dvFilters = mockObjectSet.filters, objects = listOf(mockObjectSet.obj1, mockObjectSet.obj2) ) @@ -188,9 +199,10 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { ) doReturn(Resultat.success(result)).`when`(createDataViewObject).async( CreateDataViewObject.Params.SetByRelation( - relations = listOf(mockObjectSet.relationObject3.id), + relations = listOf(setByRelationValue), filters = mockObjectSet.filters, - template = null + template = null, + type = ObjectTypeIds.PAGE ) ) doReturn(Resultat.success(Unit)).`when`(closeBlock).async(mockObjectSet.root) @@ -207,9 +219,10 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { verifyBlocking(createDataViewObject, times(1)) { async( CreateDataViewObject.Params.SetByRelation( - relations = listOf(mockObjectSet.relationObject3.id), + relations = listOf(relationSetBy.key), filters = mockObjectSet.filters, - template = null + template = null, + type = ObjectTypeIds.PAGE ) ) } @@ -252,7 +265,8 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { ) doReturn(Resultat.success(result)).`when`(createDataViewObject).async( CreateDataViewObject.Params.Collection( - templateId = null + templateId = null, + type = ObjectTypeIds.PAGE ) ) doReturn(Resultat.success(Unit)).`when`(closeBlock).async(objectCollection.root) @@ -267,7 +281,7 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { advanceUntilIdle() verifyBlocking(createDataViewObject, times(1)) { - async(CreateDataViewObject.Params.Collection(null)) + async(CreateDataViewObject.Params.Collection(type = ObjectTypeIds.PAGE, templateId = null)) } verifyBlocking(closeBlock, times(1)) { async(objectCollection.root)} diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt index 28607fcbd7..dd6bef6b8f 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt @@ -1,17 +1,19 @@ package com.anytypeio.anytype.presentation.collections import app.cash.turbine.testIn +import com.anytypeio.anytype.core_models.DV 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.StubObject +import com.anytypeio.anytype.presentation.objects.SupportedLayouts import com.anytypeio.anytype.presentation.sets.DataViewViewState import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel import com.anytypeio.anytype.presentation.sets.SetOrCollectionHeaderState import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup import com.anytypeio.anytype.presentation.sets.model.Viewer import com.anytypeio.anytype.presentation.sets.state.ObjectState -import com.anytypeio.anytype.test_utils.MockDataFactory +import com.anytypeio.anytype.presentation.sets.state.ObjectState.Companion.VIEW_DEFAULT_OBJECT_TYPE import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertIs @@ -19,6 +21,7 @@ import kotlin.test.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest +import net.bytebuddy.utility.RandomString import org.junit.After import org.junit.Before import org.junit.Test @@ -521,34 +524,41 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() { } @Test - fun `should be collection with templates present when default type is custom with proper recommended layout`() = runTest { + fun `should be collection with templates present when active view hasn't defaultTemplateId`() = runTest { // SETUP - val defaultObjectType = MockDataFactory.randomString() - val defaultObjectTypeName = "CustomName" - stubWorkspaceManager(mockObjectCollection.workspaceId) stubInterceptEvents() stubInterceptThreadStatus() - stubGetDefaultPageType(defaultObjectType, defaultObjectTypeName) stubTemplatesContainer( - type = defaultObjectType, - templates = listOf(StubObject(objectType = defaultObjectType)) + type = VIEW_DEFAULT_OBJECT_TYPE, + templates = listOf(StubObject(objectType = VIEW_DEFAULT_OBJECT_TYPE)) + ) + + session.currentViewerId.value = mockObjectCollection.viewerList.id + val dataview = mockObjectCollection.dataView.copy( + content = (mockObjectCollection.dataView.content as DV).copy( + viewers = listOf( + mockObjectCollection.viewerGrid.copy(defaultObjectType = ObjectTypeIds.PROFILE), + mockObjectCollection.viewerList.copy(defaultObjectType = ObjectTypeIds.PAGE) + ) + ) ) stubOpenObject( doc = listOf( mockObjectCollection.header, mockObjectCollection.title, - mockObjectCollection.dataView + dataview ), details = mockObjectCollection.details ) stubStoreOfObjectTypes( + VIEW_DEFAULT_OBJECT_TYPE, mapOf( - Relations.ID to defaultObjectType, + Relations.ID to VIEW_DEFAULT_OBJECT_TYPE, Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), - Relations.NAME to defaultObjectTypeName + Relations.NAME to "VIEW_DEFAULT_OBJECT_TYPE" ) ) stubStoreOfRelations(mockObjectCollection) @@ -578,29 +588,100 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() { } @Test - fun `should be collection without templates allowed when default type is custom with not proper recommended layout`() = runTest { + fun `should be collection without templates allowed when active viewer default type is NOTE`() = runTest { // SETUP - val defaultObjectType = MockDataFactory.randomString() - val defaultObjectTypeName = "CustomName" - stubWorkspaceManager(mockObjectCollection.workspaceId) stubInterceptEvents() stubInterceptThreadStatus() - stubGetDefaultPageType(defaultObjectType, defaultObjectTypeName) + stubTemplatesContainer() + + session.currentViewerId.value = mockObjectCollection.viewerList.id + val dataview = mockObjectCollection.dataView.copy( + content = (mockObjectCollection.dataView.content as DV).copy( + viewers = listOf( + mockObjectCollection.viewerGrid.copy(defaultObjectType = ObjectTypeIds.PROFILE), + mockObjectCollection.viewerList.copy(defaultObjectType = ObjectTypeIds.NOTE) + ) + ) + ) stubOpenObject( doc = listOf( mockObjectCollection.header, mockObjectCollection.title, - mockObjectCollection.dataView + dataview ), details = mockObjectCollection.details ) stubStoreOfObjectTypes( + ObjectTypeIds.NOTE, + mapOf( + Relations.ID to ObjectTypeIds.NOTE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.NOTE.code.toDouble(), + Relations.NAME to "NOTE" + ) + ) + stubStoreOfRelations(mockObjectCollection) + stubSubscriptionResults( + subscription = mockObjectCollection.subscriptionId, + workspace = mockObjectCollection.workspaceId, + collection = root, + storeOfRelations = storeOfRelations, + keys = mockObjectCollection.dvKeys, + dvSorts = mockObjectCollection.sorts + ) + + // TESTING + viewModel.onStart(ctx = root) + + val viewerFlow = viewModel.currentViewer.testIn(backgroundScope) + val stateFlow = stateReducer.state.testIn(backgroundScope) + + // ASSERT STATES + assertIs(stateFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()) + assertIs(stateFlow.awaitItem()) + + val item = viewerFlow.awaitItem() + assertIs(item) + assertFalse(item.hasTemplates) + } + + @Test + fun `should be collection without templates allowed when active viewer default type is custom type without recommended layouts`() = runTest { + // SETUP + + val defaultObjectType = RandomString.make() + val defaultObjectTypeName = RandomString.make() + + stubWorkspaceManager(mockObjectCollection.workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + + session.currentViewerId.value = mockObjectCollection.viewerList.id + val dataview = mockObjectCollection.dataView.copy( + content = (mockObjectCollection.dataView.content as DV).copy( + viewers = listOf( + mockObjectCollection.viewerGrid.copy(defaultObjectType = ObjectTypeIds.PROFILE), + mockObjectCollection.viewerList.copy(defaultObjectType = defaultObjectType) + ) + ) + ) + + stubOpenObject( + doc = listOf( + mockObjectCollection.header, + mockObjectCollection.title, + dataview + ), + details = mockObjectCollection.details + ) + stubStoreOfObjectTypes( + defaultObjectType, mapOf( Relations.ID to defaultObjectType, - Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.SET.code.toDouble(), + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.COLLECTION.code.toDouble(), Relations.NAME to defaultObjectTypeName ) ) @@ -631,29 +712,39 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() { } @Test - fun `should be collection without templates allowed when default type is NOTE`() = runTest { + fun `should be collection with templates allowed when active viewer default type is custom type with recommended layouts`() = runTest { // SETUP - val defaultObjectType = ObjectTypeIds.NOTE - val defaultObjectTypeName = "Note" + val defaultObjectType = RandomString.make() + val defaultObjectTypeName = RandomString.make() stubWorkspaceManager(mockObjectCollection.workspaceId) stubInterceptEvents() stubInterceptThreadStatus() - stubGetDefaultPageType(defaultObjectType, defaultObjectTypeName) + + session.currentViewerId.value = mockObjectCollection.viewerList.id + val dataview = mockObjectCollection.dataView.copy( + content = (mockObjectCollection.dataView.content as DV).copy( + viewers = listOf( + mockObjectCollection.viewerGrid.copy(defaultObjectType = ObjectTypeIds.PROFILE), + mockObjectCollection.viewerList.copy(defaultObjectType = defaultObjectType) + ) + ) + ) stubOpenObject( doc = listOf( mockObjectCollection.header, mockObjectCollection.title, - mockObjectCollection.dataView + dataview ), details = mockObjectCollection.details ) stubStoreOfObjectTypes( + defaultObjectType, mapOf( Relations.ID to defaultObjectType, - Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.NOTE.code.toDouble(), + Relations.RECOMMENDED_LAYOUT to SupportedLayouts.editorLayouts.random().code.toDouble(), Relations.NAME to defaultObjectTypeName ) ) @@ -680,6 +771,6 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() { val item = viewerFlow.awaitItem() assertIs(item) - assertFalse(item.hasTemplates) + assertTrue(item.hasTemplates) } } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateSetViewTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateSetViewTest.kt index 5d3d9ab9e5..d4c9ca2e7a 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateSetViewTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateSetViewTest.kt @@ -439,7 +439,7 @@ class ObjectStateSetViewTest : ObjectSetViewModelTestSetup() { sources = listOf(ObjectTypeIds.PAGE), dvFilters = mockObjectSet.filters ) - stubStoreOfObjectTypes(pageTypeMap) + stubStoreOfObjectTypes(ObjectTypeIds.PAGE, pageTypeMap) stubTemplatesContainer( type = ObjectTypeIds.PAGE, templates = listOf(StubObject(objectType = ObjectTypeIds.PAGE)) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt index 96a4109356..61fd7f45ed 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt @@ -4,6 +4,7 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.DVSort import com.anytypeio.anytype.core_models.Event +import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.StubDataView import com.anytypeio.anytype.core_models.StubDataViewView @@ -98,7 +99,8 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Sort.Type.DESC ) ), - filters = listOf() + filters = listOf(), + defaultObjectType = ObjectTypeIds.PAGE ) val dataView = Block( @@ -226,7 +228,8 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Sort.Type.ASC ) ), - filters = listOf() + filters = listOf(), + defaultObjectType = ObjectTypeIds.PAGE ) val viewerList = Block.Content.DataView.Viewer( @@ -273,7 +276,8 @@ class ObjectSetReducerTest { ) ), filters = listOf(), - viewerRelations = viewerGrid.viewerRelations + viewerRelations = viewerGrid.viewerRelations, + defaultObjectType = ObjectTypeIds.PAGE ) ) @@ -295,7 +299,8 @@ class ObjectSetReducerTest { type = viewerGrid.type, viewerRelations = viewerGrid.viewerRelations, sorts = viewerGrid.sorts, - filters = viewerGrid.filters + filters = viewerGrid.filters, + defaultObjectType = ObjectTypeIds.PAGE ), Block.Content.DataView.Viewer( id = viewerList.id, @@ -308,7 +313,8 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Sort.Type.DESC ) ), - filters = listOf() + filters = listOf(), + defaultObjectType = ObjectTypeIds.PAGE ) ) ), @@ -433,7 +439,8 @@ class ObjectSetReducerTest { type = viewer1.type, viewerRelations = viewer1.viewerRelations, sorts = expectedSorts, - filters = viewer1.filters + filters = viewer1.filters, + defaultObjectType = ObjectTypeIds.PAGE ), Block.Content.DataView.Viewer( id = viewer2.id, @@ -441,7 +448,8 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Viewer.Type.GRID, viewerRelations = viewer2.viewerRelations, sorts = listOf(sort1), - filters = listOf() + filters = listOf(), + defaultObjectType = ObjectTypeIds.PAGE ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -553,7 +561,8 @@ class ObjectSetReducerTest { type = viewer1.type, viewerRelations = viewer1.viewerRelations, sorts = viewer1.sorts, - filters = expectedFilters + filters = expectedFilters, + defaultObjectType = ObjectTypeIds.PAGE ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -660,7 +669,8 @@ class ObjectSetReducerTest { type = viewer1.type, viewerRelations = expectedRelations, sorts = viewer1.sorts, - filters = viewer1.filters + filters = viewer1.filters, + defaultObjectType = ObjectTypeIds.PAGE ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -759,7 +769,8 @@ class ObjectSetReducerTest { name = newViewerName, cardSize = newCardSize, coverFit = newCoverFit, - coverRelationKey = relationKey1 + coverRelationKey = relationKey1, + defaultObjectType = ObjectTypeIds.PAGE ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -837,7 +848,8 @@ class ObjectSetReducerTest { sorts = viewer.sorts, filters = viewer.filters, name = viewer.name, - coverRelationKey = viewer.coverRelationKey + coverRelationKey = viewer.coverRelationKey, + defaultObjectType = ObjectTypeIds.PAGE ) ), relationLinks = listOf(relationLink1, relationLink3), diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetTemplatesMenuTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetTemplatesMenuTest.kt index a359679cf9..15e15dab54 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetTemplatesMenuTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetTemplatesMenuTest.kt @@ -57,7 +57,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() { sources = listOf(ObjectTypeIds.PAGE), dvFilters = mockObjectSet.filters ) - stubStoreOfObjectTypes(pageTypeMap) + stubStoreOfObjectTypes(ObjectTypeIds.PAGE, pageTypeMap) stubTemplatesContainer( type = ObjectTypeIds.PAGE, templates = listOf(templateView) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ViewerDefaultObjectTypeTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ViewerDefaultObjectTypeTest.kt new file mode 100644 index 0000000000..f6a84ac05f --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ViewerDefaultObjectTypeTest.kt @@ -0,0 +1,296 @@ +package com.anytypeio.anytype.presentation.sets + +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.StubDataView +import com.anytypeio.anytype.presentation.collections.MockCollection +import com.anytypeio.anytype.presentation.collections.MockSet +import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup +import com.anytypeio.anytype.presentation.sets.state.ObjectState.Companion.VIEW_DEFAULT_OBJECT_TYPE +import com.anytypeio.anytype.test_utils.MockDataFactory +import kotlin.test.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import net.bytebuddy.utility.RandomString +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.MockitoAnnotations + + +@OptIn(ExperimentalCoroutinesApi::class) +class ViewerDefaultObjectTypeTest : ObjectSetViewModelTestSetup() { + + private lateinit var closable: AutoCloseable + private lateinit var viewModel: ObjectSetViewModel + private lateinit var mockSetByType: MockSet + private lateinit var mockSetByRelation: MockSet + private lateinit var mockCollection: MockCollection + + val defaultTypeId = VIEW_DEFAULT_OBJECT_TYPE + val customType1Id = "customType1-${RandomString.make()}" + val customType2Id = "customType2-${RandomString.make()}" + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "customType1" + ) + + val customType2Map = mapOf( + Relations.ID to customType2Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.SET.code.toDouble(), + Relations.NAME to "customType2" + ) + + val pageTypeMap = mapOf( + Relations.ID to defaultTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString() + ) + + @Before + fun setup() { + closable = MockitoAnnotations.openMocks(this) + viewModel = givenViewModel() + } + + @After + fun after() { + rule.advanceTime() + closable.close() + } + + @Test + fun `set by type should has this type for all views default object type ids`() = runTest { + + val setOfValue = MockDataFactory.randomUuid() + mockSetByType = MockSet(context = root, setOfValue = setOfValue) + + with(mockSetByType) { + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + + with(storeOfObjectTypes) { + set(defaultTypeId, pageTypeMap) + set(customType1Id, customType1Map) + set(customType2Id, customType2Map) + } + + val viewer1 = viewerGrid.copy(defaultObjectType = customType1Id) + val viewer2 = viewerGallery.copy(defaultObjectType = customType2Id) + val viewer3 = viewerList.copy(defaultObjectType = ObjectTypeIds.PAGE) + + val dataViewWith3Views = StubDataView( + id = "dv-${RandomString.make()}", + views = listOf(viewer1, viewer2, viewer3), + relationLinks = listOf( + relationLink1, + relationLink2, + relationLink3, + relationLink4, + relationLink5 + ) + ) + stubOpenObject( + doc = listOf(header, title, dataViewWith3Views), + details = details + ) + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setOfValue), + dvFilters = filters + ) + stubTemplatesContainer() + } + + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.viewersWidgetState.value + + val viewer1 = result.items[0] + val viewer2 = result.items[1] + val viewer3 = result.items[2] + + assertEquals( + expected = setOfValue, + actual = viewer1.defaultObjectType + ) + assertEquals( + expected = setOfValue, + actual = viewer2.defaultObjectType + ) + assertEquals( + expected = setOfValue, + actual = viewer3.defaultObjectType + ) + assertEquals( + expected = 3, + actual = result.items.size + ) + } + + @Test + fun `set by relation should has proper views default object type ids`() = runTest { + + mockSetByRelation = MockSet(context = root, setOfValue = MockSet("").relationObject3.id) + with(mockSetByRelation) { + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + + with(storeOfObjectTypes) { + set(defaultTypeId, pageTypeMap) + set(customType1Id, customType1Map) + set(customType2Id, customType2Map) + } + + val viewer1 = viewerGrid.copy(defaultObjectType = customType1Id) + val viewer2 = viewerGallery.copy(defaultObjectType = customType2Id) + val viewer3 = viewerList.copy(defaultObjectType = ObjectTypeIds.PAGE) + + val dataViewWith3Views = StubDataView( + id = "dv-${RandomString.make()}", + views = listOf(viewer1, viewer2, viewer3), + relationLinks = listOf( + relationLink1, + relationLink2, + relationLink3, + relationLink4, + relationLink5 + ) + ) + stubOpenObject( + doc = listOf(header, title, dataViewWith3Views), + details = detailsSetByRelation + ) + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(relationObject3.id), + dvFilters = filters + ) + stubTemplatesContainer() + } + + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.viewersWidgetState.value + + val viewer1 = result.items[0] + val viewer2 = result.items[1] + val viewer3 = result.items[2] + + assertEquals( + expected = customType1Id, + actual = viewer1.defaultObjectType + ) + assertEquals( + expected = customType2Id, + actual = viewer2.defaultObjectType + ) + assertEquals( + expected = defaultTypeId, + actual = viewer3.defaultObjectType + ) + assertEquals( + expected = 3, + actual = result.items.size + ) + } + + @Test + fun `collection should has proper views default object type ids`() = runTest { + + mockCollection = MockCollection(context = root) + with(mockCollection) { + stubWorkspaceManager(workspaceId) + stubStoreOfRelations(this) + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + + with(storeOfObjectTypes) { + set(defaultTypeId, pageTypeMap) + set(customType1Id, customType1Map) + set(customType2Id, customType2Map) + } + + val viewer1 = viewerGrid.copy(defaultObjectType = customType1Id) + val viewer2 = viewerGallery.copy(defaultObjectType = customType2Id) + val viewer3 = viewerList.copy(defaultObjectType = ObjectTypeIds.PAGE) + + session.currentViewerId.value = viewer3.id + + val dataViewWith3Views = StubDataView( + id = "dv-${RandomString.make()}", + views = listOf(viewer1, viewer2, viewer3), + relationLinks = listOf( + relationLink1, + relationLink2, + relationLink3, + relationLink4, + relationLink5, + relationLink6 + ) + ) + stubOpenObject( + doc = listOf(header, title, dataViewWith3Views), + details = details + ) + stubSubscriptionResults( + subscription = this.subscriptionId, + collection = root, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + objects = listOf(obj1, obj2), + dvSorts = sorts + ) + stubTemplatesContainer() + } + + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.viewersWidgetState.value + + val viewer1 = result.items[0] + val viewer2 = result.items[1] + val viewer3 = result.items[2] + + assertEquals( + expected = customType1Id, + actual = viewer1.defaultObjectType + ) + assertEquals( + expected = customType2Id, + actual = viewer2.defaultObjectType + ) + assertEquals( + expected = defaultTypeId, + actual = viewer3.defaultObjectType + ) + assertEquals( + expected = 3, + actual = result.items.size + ) + } +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt index 26382df976..ac9bb6228f 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt @@ -29,6 +29,7 @@ import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection import com.anytypeio.anytype.domain.`object`.DuplicateObjects import com.anytypeio.anytype.domain.`object`.UpdateDetail import com.anytypeio.anytype.domain.objects.DefaultObjectStore +import com.anytypeio.anytype.domain.objects.DefaultStoreOfObjectTypes import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations import com.anytypeio.anytype.domain.objects.ObjectStore import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived @@ -150,7 +151,6 @@ open class ObjectSetViewModelTestSetup { @Mock lateinit var addObjectToCollection: AddObjectToCollection - @Mock lateinit var storeOfObjectTypes: StoreOfObjectTypes @Mock @@ -211,6 +211,7 @@ open class ObjectSetViewModelTestSetup { dispatchers = dispatchers ) dataViewSubscription = DefaultDataViewSubscription(dataViewSubscriptionContainer) + storeOfObjectTypes = DefaultStoreOfObjectTypes() return ObjectSetViewModel( openObjectSet = openObjectSet, closeBlock = closeBlock, @@ -371,10 +372,8 @@ open class ObjectSetViewModelTestSetup { ) } - fun stubStoreOfObjectTypes(map: Map = emptyMap()) { - storeOfObjectTypes.stub { - onBlocking { get(any()) } doReturn ObjectWrapper.Type(map = map) - } + suspend fun stubStoreOfObjectTypes(id: String = "", map: Map = emptyMap()) { + storeOfObjectTypes.set(id, map) } fun stubGetDefaultPageType(type: String = defaultObjectPageType, name: String = defaultObjectPageTypeName) { diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt index 33541f3195..e157a50c43 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.presentation.sets.main import app.cash.turbine.test +import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject import com.anytypeio.anytype.presentation.collections.MockSet import com.anytypeio.anytype.presentation.sets.DataViewViewState @@ -115,7 +116,8 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() { CreateDataViewObject.Params.SetByRelation( relations = listOf(mockObjectSet.relationObject3.id), filters = mockObjectSet.filters, - template = null + template = null, + type = ObjectTypeIds.PAGE ) ) @@ -138,7 +140,8 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() { CreateDataViewObject.Params.SetByRelation( relations = listOf(mockObjectSet.relationObject3.id), filters = mockObjectSet.filters, - template = null + template = null, + type = ObjectTypeIds.PAGE ) ) } diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt index 789054823e..c052b4fb8c 100644 --- a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt @@ -32,7 +32,8 @@ fun StubDataViewView( cardSize: DVViewerCardSize = DVViewerCardSize.SMALL, hideIcon: Boolean = false, coverFit: Boolean = false, - coverRelationKey: String? = null + coverRelationKey: String? = null, + defaultObjectType: Id = ObjectTypeIds.PAGE ): DVViewer = DVViewer( id = id, filters = filters, @@ -43,7 +44,8 @@ fun StubDataViewView( cardSize = cardSize, hideIcon = hideIcon, coverFit = coverFit, - coverRelationKey = coverRelationKey + coverRelationKey = coverRelationKey, + defaultObjectType = defaultObjectType ) fun StubDataViewViewRelation(