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

DROID-1620 Collection | Enhancement | View's default object type (#332)

This commit is contained in:
Konstantin Ivanov 2023-09-04 11:54:14 +02:00 committed by GitHub
parent b3889e33c7
commit 2d1c206c63
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 662 additions and 240 deletions

View file

@ -209,10 +209,8 @@ abstract class TestObjectSetSetup {
dispatchers = dispatchers
)
createDataViewObject = CreateDataViewObject(
getTemplates = getTemplates,
repo = repo,
storeOfRelations = storeOfRelations,
getDefaultPageType = getDefaultPageType,
dispatchers = dispatchers
)
setObjectDetails = UpdateDetail(repo)

View file

@ -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
)

View file

@ -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) {

View file

@ -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<CreateDataViewObject.Params, CreateDataViewObject.Result>(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<DVFilter>,
relations: List<Key>,
@ -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<DVFilter>,
val relations: List<Id>,
val template: Id?
val template: Id?,
val type: Id?
) : Params()
data class Collection(
val type: Id?,
val templateId: Id?
) : Params()
}

View file

@ -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(

View file

@ -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) {

View file

@ -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<Id>): Lis
return query.filter(::isValidObject)
}
suspend fun ObjectState.DataView.Set.isTemplatesAllowed(
setOfValue: List<Id>,
storeOfObjectTypes: StoreOfObjectTypes,
getDefaultPageType: GetDefaultPageType
): Boolean {
fun ObjectState.DataView.Set.isSetByRelation(setOfValue: List<Id>): 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<DVViewer>.toView(session: ObjectSetSession): List<ViewerView> {
return mapIndexed { index, viewer ->
fun ObjectState.DataView.toViewersView(ctx: Id, session: ObjectSetSession): List<ViewerView> {
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<DVViewer>,
session: ObjectSetSession
): List<ViewerView> {
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<ViewerView>.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()
}

View file

@ -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<TemplateView>
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<TemplateView>
): 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<ObjectState.DataView>(),
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<ObjectWrapper.Basic>,
viewerDefObjType: ObjectWrapper.Type,
viewer: DVViewer
): List<TemplateView> {
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(

View file

@ -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
}
}

View file

@ -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?
)

View file

@ -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

View file

@ -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(

View file

@ -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
)
)
)
)
}

View file

@ -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)}

View file

@ -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<ObjectState.Init>(stateFlow.awaitItem())
assertIs<DataViewViewState.Init>(viewerFlow.awaitItem())
assertIs<ObjectState.DataView.Collection>(stateFlow.awaitItem())
val item = viewerFlow.awaitItem()
assertIs<DataViewViewState.Collection.NoItems>(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<DataViewViewState.Collection.NoItems>(item)
assertFalse(item.hasTemplates)
assertTrue(item.hasTemplates)
}
}

View file

@ -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))

View file

@ -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),

View file

@ -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)

View file

@ -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
)
}
}

View file

@ -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<String, Any?> = emptyMap()) {
storeOfObjectTypes.stub {
onBlocking { get(any()) } doReturn ObjectWrapper.Type(map = map)
}
suspend fun stubStoreOfObjectTypes(id: String = "", map: Map<String, Any?> = emptyMap()) {
storeOfObjectTypes.set(id, map)
}
fun stubGetDefaultPageType(type: String = defaultObjectPageType, name: String = defaultObjectPageTypeName) {

View file

@ -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
)
)
}

View file

@ -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(