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

DROID-3336 Primitives | Editor and Templates, getting featured ids from Type (#2197)

This commit is contained in:
Konstantin Ivanov 2025-03-27 12:58:00 +01:00 committed by GitHub
parent f2d5cb4c65
commit 06182019b0
Signed by: github
GPG key ID: B5690EEEBB952194
23 changed files with 837 additions and 977 deletions

View file

@ -52,9 +52,15 @@ object TemplateBlankModule {
@TemplateBlankScope
@Provides
fun provideViewModelFactory(
renderer: DefaultBlockViewRenderer
renderer: DefaultBlockViewRenderer,
storeOfRelations: StoreOfRelations,
storeOfObjectTypes: StoreOfObjectTypes,
fieldParser: FieldParser
): ViewModelProvider.Factory = TemplateBlankViewModelFactory(
renderer = renderer
renderer = renderer,
storeOfRelations = storeOfRelations,
storeOfObjectTypes = storeOfObjectTypes,
fieldParser = fieldParser
)
@Module

View file

@ -23,7 +23,7 @@ data class FieldDateValue(
val relativeDate: RelativeDate
)
data class ParsedFields(
data class ParsedProperties(
val header: List<ObjectWrapper.Relation> = emptyList(),
val sidebar: List<ObjectWrapper.Relation> = emptyList(),
val hidden: List<ObjectWrapper.Relation> = emptyList(),

View file

@ -12,7 +12,7 @@ import com.anytypeio.anytype.core_models.SupportedLayouts
import com.anytypeio.anytype.core_models.TimeInSeconds
import com.anytypeio.anytype.core_models.primitives.Field
import com.anytypeio.anytype.core_models.primitives.FieldDateValue
import com.anytypeio.anytype.core_models.primitives.ParsedFields
import com.anytypeio.anytype.core_models.primitives.ParsedProperties
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.primitives.TimestampInSeconds
import com.anytypeio.anytype.core_models.primitives.Value
@ -43,21 +43,21 @@ interface FieldParser {
types: List<ObjectWrapper.Type>
): Pair<Id?, String?>
suspend fun getObjectParsedFields(
suspend fun getObjectParsedProperties(
objectType: ObjectWrapper.Type,
objFieldKeys: List<Key>,
objPropertiesKeys: List<Key>,
storeOfRelations: StoreOfRelations
): ParsedFields
): ParsedProperties
suspend fun getObjectTypeParsedFields(
suspend fun getObjectTypeParsedProperties(
objectType: ObjectWrapper.Type,
objectTypeConflictingFieldsIds: List<Id>,
objectTypeConflictingPropertiesIds: List<Id>,
storeOfRelations: StoreOfRelations
): ParsedFields
): ParsedProperties
fun isFieldEditable(relation: ObjectWrapper.Relation): Boolean
fun isPropertyEditable(property: ObjectWrapper.Relation): Boolean
fun isFieldCanBeDeletedFromType(field: ObjectWrapper.Relation): Boolean
fun isPropertyCanBeDeletedFromType(property: ObjectWrapper.Relation): Boolean
}
class FieldParserImpl @Inject constructor(
@ -200,14 +200,14 @@ class FieldParserImpl @Inject constructor(
}
//endregion
//region Parsed fields
//region Parsed properties
// Consolidated function to build ParsedFields.
private suspend fun getParsedFields(
// Consolidated function to build Parsed Properties.
private suspend fun getParsedProperties(
objType: ObjectWrapper.Type,
localFieldIds: Collection<Id>,
localPropertiesIds: Collection<Id>,
storeOfRelations: StoreOfRelations
): ParsedFields {
): ParsedProperties {
// Clean recommended IDs based on priority.
// recommendedFeaturedRelations always remain.
@ -228,46 +228,46 @@ class FieldParserImpl @Inject constructor(
.filter { it !in featuredIds && it !in relationsIds && it !in fileIds }
.distinct()
// Fetch valid relations for each recommended group.
val headerFields = storeOfRelations.getValidRelations(ids = featuredIds)
val sidebarFields = storeOfRelations.getValidRelations(ids = relationsIds)
val fileFields = storeOfRelations.getValidRelations(ids = fileIds)
val hiddenFields = storeOfRelations.getValidRelations(ids = hiddenIds)
// Fetch valid properties for each recommended group.
val headerProperties = storeOfRelations.getValidRelations(ids = featuredIds)
val sidebarProperties = storeOfRelations.getValidRelations(ids = relationsIds)
val fileProperties = storeOfRelations.getValidRelations(ids = fileIds)
val hiddenProperties = storeOfRelations.getValidRelations(ids = hiddenIds)
// Combine IDs from all recommended relations.
val existingIds = (headerFields + sidebarFields + hiddenFields + fileFields)
// Combine IDs from all recommended properties.
val existingIds = (headerProperties + sidebarProperties + hiddenProperties + fileProperties)
.map { it.id }
.toSet()
// Filter out fields already present in the recommended groups.
val allLocalFields = storeOfRelations.getValidRelations(
ids = localFieldIds
// Filter out properties already present in the recommended groups.
val allLocalProperties = storeOfRelations.getValidRelations(
ids = localPropertiesIds
.filter { it !in existingIds }
.toList()
)
// Partition local fields into system and non-system fields.
val (localSystemFields, localFieldsWithoutSystem) = allLocalFields.partition {
// Partition local properties into system and non-system properties.
val (localSystemProperties, localPropertiesWithoutSystem) = allLocalProperties.partition {
Relations.systemRelationKeys.contains(it.key)
}
return ParsedFields(
header = headerFields,
sidebar = sidebarFields,
hidden = hiddenFields,
localWithoutSystem = localFieldsWithoutSystem,
localSystem = localSystemFields,
file = fileFields
return ParsedProperties(
header = headerProperties,
sidebar = sidebarProperties,
hidden = hiddenProperties,
localWithoutSystem = localPropertiesWithoutSystem,
localSystem = localSystemProperties,
file = fileProperties
)
}
override suspend fun getObjectParsedFields(
override suspend fun getObjectParsedProperties(
objectType: ObjectWrapper.Type,
objFieldKeys: List<Key>,
objPropertiesKeys: List<Key>,
storeOfRelations: StoreOfRelations
): ParsedFields {
): ParsedProperties {
val localFieldIds = storeOfRelations.getByKeys(
keys = objFieldKeys
keys = objPropertiesKeys
).mapNotNull {
if (it.isValidToUse) {
it.id
@ -275,26 +275,26 @@ class FieldParserImpl @Inject constructor(
null
}
}
return getParsedFields(
return getParsedProperties(
objType = objectType,
localFieldIds = localFieldIds,
localPropertiesIds = localFieldIds,
storeOfRelations = storeOfRelations
)
}
override suspend fun getObjectTypeParsedFields(
override suspend fun getObjectTypeParsedProperties(
objectType: ObjectWrapper.Type,
objectTypeConflictingFieldsIds: List<Id>,
objectTypeConflictingPropertiesIds: List<Id>,
storeOfRelations: StoreOfRelations
): ParsedFields {
return getParsedFields(
): ParsedProperties {
return getParsedProperties(
objType = objectType,
localFieldIds = objectTypeConflictingFieldsIds,
localPropertiesIds = objectTypeConflictingPropertiesIds,
storeOfRelations = storeOfRelations
)
}
override fun isFieldEditable(relation: ObjectWrapper.Relation): Boolean {
override fun isPropertyEditable(relation: ObjectWrapper.Relation): Boolean {
return !(relation.isReadOnly == true ||
relation.isHidden == true ||
relation.isArchived == true ||
@ -302,8 +302,8 @@ class FieldParserImpl @Inject constructor(
Relations.systemRelationKeys.contains(relation.key))
}
override fun isFieldCanBeDeletedFromType(field: ObjectWrapper.Relation): Boolean {
return !(field.isHidden == true || Relations.systemRelationKeys.contains(field.key))
override fun isPropertyCanBeDeletedFromType(property: ObjectWrapper.Relation): Boolean {
return !(property.isHidden == true || Relations.systemRelationKeys.contains(property.key))
}
//endregion
}

View file

@ -16,9 +16,7 @@ import com.anytypeio.anytype.domain.resources.StringResourceProvider
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem.Item
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem.Section
import com.anytypeio.anytype.feature_properties.edit.UiPropertyLimitTypeItem
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.mapper.objectIcon
import com.anytypeio.anytype.presentation.relations.BasicObjectCoverWrapper
import com.anytypeio.anytype.presentation.relations.getCover
import com.anytypeio.anytype.presentation.templates.TemplateView
@ -52,7 +50,7 @@ fun ObjectWrapper.Basic.toTemplateView(
//endregion
/**
* Extension function to safely get a name for the relation.
* Extension function to safely get a name for the property.
* If the name is blank, returns a default untitled title.
*/
fun ObjectWrapper.Relation.getName(stringResourceProvider: StringResourceProvider): String =
@ -62,50 +60,50 @@ fun ObjectWrapper.Relation.getName(stringResourceProvider: StringResourceProvide
name!!
}
suspend fun buildUiFieldsList(
suspend fun buildUiPropertiesList(
objType: ObjectWrapper.Type,
stringResourceProvider: StringResourceProvider,
fieldParser: FieldParser,
storeOfObjectTypes: StoreOfObjectTypes,
storeOfRelations: StoreOfRelations,
objTypeConflictingFields: List<Id>,
showHiddenFields: Boolean
objectTypeConflictingPropertiesIds: List<Id>,
showHiddenProperty: Boolean
): List<UiFieldsListItem> {
val parsedFields = fieldParser.getObjectTypeParsedFields(
val parsedProperties = fieldParser.getObjectTypeParsedProperties(
objectType = objType,
storeOfRelations = storeOfRelations,
objectTypeConflictingFieldsIds = objTypeConflictingFields
objectTypeConflictingPropertiesIds = objectTypeConflictingPropertiesIds
)
// The mapping functions already skip the Relations.DESCRIPTION key.
val headerItems = parsedFields.header.mapNotNull {
mapToUiFieldsDraggableListItem(
field = it,
val headerItems = parsedProperties.header.mapNotNull {
mapToUiPropertiesDraggableListItem(
property = it,
stringResourceProvider = stringResourceProvider,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
)
}
val sidebarItems = parsedFields.sidebar.mapNotNull {
mapToUiFieldsDraggableListItem(
field = it,
val sidebarItems = parsedProperties.sidebar.mapNotNull {
mapToUiPropertiesDraggableListItem(
property = it,
stringResourceProvider = stringResourceProvider,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
)
}
val hiddenItems = parsedFields.hidden.mapNotNull {
mapToUiFieldsDraggableListItem(
field = it,
val hiddenItems = parsedProperties.hidden.mapNotNull {
mapToUiPropertiesDraggableListItem(
property = it,
stringResourceProvider = stringResourceProvider,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
)
}
val conflictedItems = parsedFields.localWithoutSystem.mapNotNull {
mapToUiFieldsLocalListItem(
field = it,
val conflictedItems = parsedProperties.localWithoutSystem.mapNotNull {
mapToUiPropertiesLocalListItem(
property = it,
stringResourceProvider = stringResourceProvider,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
@ -113,18 +111,18 @@ suspend fun buildUiFieldsList(
}
//this items goes to the Hidden section as draggable items
val conflictedSystemItems = parsedFields.localSystem.mapNotNull {
mapToUiFieldsDraggableListItem(
field = it,
val conflictedSystemItems = parsedProperties.localSystem.mapNotNull {
mapToUiPropertiesDraggableListItem(
property = it,
stringResourceProvider = stringResourceProvider,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
)
}
val fileRecommendedFields = parsedFields.file.mapNotNull {
mapToUiFieldsDraggableListItem(
field = it,
val fileRecommendedFields = parsedProperties.file.mapNotNull {
mapToUiPropertiesDraggableListItem(
property = it,
stringResourceProvider = stringResourceProvider,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
@ -144,7 +142,7 @@ suspend fun buildUiFieldsList(
// addAll(fileRecommendedFields)
// }
if (showHiddenFields) {
if (showHiddenProperty) {
add(Section.Hidden(canAdd = false))
addAll(hiddenItems)
addAll(conflictedSystemItems)
@ -158,14 +156,14 @@ suspend fun buildUiFieldsList(
}
/**
* Shared helper to build the limit object types for a field.
* Shared helper to build the limit object types for a property.
*/
private suspend fun mapLimitObjectTypes(
relation: ObjectWrapper.Relation,
property: ObjectWrapper.Relation,
storeOfObjectTypes: StoreOfObjectTypes
): List<Id> {
return if (relation.format == RelationFormat.OBJECT && relation.relationFormatObjectTypes.isNotEmpty()) {
relation.relationFormatObjectTypes.mapNotNull { id ->
return if (property.format == RelationFormat.OBJECT && property.relationFormatObjectTypes.isNotEmpty()) {
property.relationFormatObjectTypes.mapNotNull { id ->
storeOfObjectTypes.get(id)?.let { objType ->
if (objType.isValid) {
objType.id
@ -180,47 +178,53 @@ private suspend fun mapLimitObjectTypes(
}
/**
* Maps a field to a draggable UI list item.
* Returns null if the field key equals DESCRIPTION.
* Maps a property to a draggable UI list item.
* Returns null if the property key equals DESCRIPTION.
*/
private suspend fun mapToUiFieldsDraggableListItem(
field: ObjectWrapper.Relation,
private suspend fun mapToUiPropertiesDraggableListItem(
property: ObjectWrapper.Relation,
stringResourceProvider: StringResourceProvider,
storeOfObjectTypes: StoreOfObjectTypes,
fieldParser: FieldParser
): UiFieldsListItem? {
if (field.key == Relations.DESCRIPTION) return null
if (property.key == Relations.DESCRIPTION) return null
return Item.Draggable(
id = field.id,
fieldKey = field.key,
fieldTitle = field.getName(stringResourceProvider),
format = field.format,
limitObjectTypes = mapLimitObjectTypes(field, storeOfObjectTypes),
isEditableField = fieldParser.isFieldEditable(field),
isPossibleToUnlinkFromType = fieldParser.isFieldCanBeDeletedFromType(field)
id = property.id,
fieldKey = property.key,
fieldTitle = property.getName(stringResourceProvider),
format = property.format,
limitObjectTypes = mapLimitObjectTypes(
property = property,
storeOfObjectTypes = storeOfObjectTypes
),
isEditableField = fieldParser.isPropertyEditable(property),
isPossibleToUnlinkFromType = fieldParser.isPropertyCanBeDeletedFromType(property)
)
}
/**
* Maps a field to a local UI list item.
* Returns null if the field key equals DESCRIPTION.
* Maps a property to a local UI list item.
* Returns null if the property key equals DESCRIPTION.
*/
private suspend fun mapToUiFieldsLocalListItem(
field: ObjectWrapper.Relation,
private suspend fun mapToUiPropertiesLocalListItem(
property: ObjectWrapper.Relation,
stringResourceProvider: StringResourceProvider,
storeOfObjectTypes: StoreOfObjectTypes,
fieldParser: FieldParser,
): UiFieldsListItem? {
if (field.key == Relations.DESCRIPTION) return null
if (property.key == Relations.DESCRIPTION) return null
return Item.Local(
id = field.id,
fieldKey = field.key,
fieldTitle = field.getName(stringResourceProvider),
format = field.format,
limitObjectTypes = mapLimitObjectTypes(field, storeOfObjectTypes),
isEditableField = fieldParser.isFieldEditable(field)
id = property.id,
fieldKey = property.key,
fieldTitle = property.getName(stringResourceProvider),
format = property.format,
limitObjectTypes = mapLimitObjectTypes(
property = property,
storeOfObjectTypes = storeOfObjectTypes
),
isEditableField = fieldParser.isPropertyEditable(property)
)
}

View file

@ -49,7 +49,7 @@ import com.anytypeio.anytype.feature_object_type.ui.UiSyncStatusBadgeState
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesButtonState
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesModalListState
import com.anytypeio.anytype.feature_object_type.ui.UiTitleState
import com.anytypeio.anytype.feature_object_type.ui.buildUiFieldsList
import com.anytypeio.anytype.feature_object_type.ui.buildUiPropertiesList
import com.anytypeio.anytype.feature_object_type.ui.toTemplateView
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState.Visible.View
@ -328,14 +328,14 @@ class ObjectTypeViewModel(
}
}
updateDefaultTemplates(defaultTemplate = objType.defaultTemplateId)
val items = buildUiFieldsList(
val items = buildUiPropertiesList(
objType = objType,
stringResourceProvider = stringResourceProvider,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes,
storeOfRelations = storeOfRelations,
objTypeConflictingFields = conflictingFields,
showHiddenFields = vmParams.showHiddenFields
objectTypeConflictingPropertiesIds = conflictingFields,
showHiddenProperty = vmParams.showHiddenFields
)
uiFieldsListState.value = UiFieldsListState(items = items)
uiFieldsButtonState.value = UiFieldsButtonState.Visible(

View file

@ -181,10 +181,10 @@ class TestFieldsMappping {
merge(listOf(testObjectType, fieldAssigneeObjType2, fieldAssigneeObjType1))
}
val parsedFields = fieldParser.getObjectTypeParsedFields(
val parsedFields = fieldParser.getObjectTypeParsedProperties(
objectType = testObjectType,
storeOfRelations = storeOfRelations,
objectTypeConflictingFieldsIds = listOf()
objectTypeConflictingPropertiesIds = listOf()
)
assertEquals(
@ -204,10 +204,10 @@ class TestFieldsMappping {
merge(listOf(testObjectType, fieldAssigneeObjType2, fieldAssigneeObjType1))
}
val parsedFields = fieldParser.getObjectTypeParsedFields(
val parsedFields = fieldParser.getObjectTypeParsedProperties(
objectType = testObjectType,
storeOfRelations = storeOfRelations,
objectTypeConflictingFieldsIds = listOf()
objectTypeConflictingPropertiesIds = listOf()
)
assertEquals(
@ -235,10 +235,10 @@ class TestFieldsMappping {
merge(listOf(testObjectType, fieldAssigneeObjType2, fieldAssigneeObjType1))
}
val parsedFields = fieldParser.getObjectTypeParsedFields(
val parsedFields = fieldParser.getObjectTypeParsedProperties(
objectType = testObjectType,
storeOfRelations = storeOfRelations,
objectTypeConflictingFieldsIds = listOf()
objectTypeConflictingPropertiesIds = listOf()
)
assertEquals(

View file

@ -30,6 +30,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.InEditor
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Mode
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
import com.anytypeio.anytype.presentation.extension.getTypeForObject
import com.anytypeio.anytype.presentation.mapper.objectIcon
import com.anytypeio.anytype.presentation.mapper.marks
import com.anytypeio.anytype.presentation.mapper.toFileView
@ -38,6 +39,7 @@ import com.anytypeio.anytype.presentation.mapper.toVideoView
import com.anytypeio.anytype.presentation.mapper.toView
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.appearance.LinkAppearanceFactory
import com.anytypeio.anytype.presentation.objects.getFeaturedPropertiesIds
import com.anytypeio.anytype.presentation.objects.getProperType
import com.anytypeio.anytype.presentation.relations.BasicObjectCoverWrapper
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
@ -680,7 +682,7 @@ class DefaultBlockViewRenderer @Inject constructor(
restrictions = restrictions,
selection = selection,
count = mCounter,
onRenderFlag = onRenderFlag,
onRenderFlag = onRenderFlag
)
)
}
@ -711,12 +713,10 @@ class DefaultBlockViewRenderer @Inject constructor(
val featured = featured(
ctx = root.id,
block = block,
details = details,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
details = details
)
if (featured.relations.isNotEmpty()) {
if (!featured?.relations.isNullOrEmpty()) {
result.add(featured)
}
}
@ -2077,25 +2077,19 @@ class DefaultBlockViewRenderer @Inject constructor(
private suspend fun featured(
ctx: Id,
block: Block,
details: ObjectViewDetails,
fieldParser: FieldParser,
storeOfObjectTypes: StoreOfObjectTypes
): BlockView.FeaturedRelation {
val obj = details.getObject(ctx)
val featuredKeys = workaroundGlobalNameOrIdentityRelation(obj?.featuredRelations.orEmpty(), obj?.map.orEmpty())
details: ObjectViewDetails
): BlockView.FeaturedRelation? {
val obj = details.getObject(ctx) ?: return null
val views = mapFeaturedRelations(
ctx = ctx,
keys = featuredKeys,
details = details,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
).sortedByDescending { it.key == Relations.TYPE || it.key == Relations.GLOBAL_NAME || it.key == Relations.IDENTITY }
currentObject = obj
)
return BlockView.FeaturedRelation(
id = block.id,
relations = views,
allowChangingObjectType = obj?.type?.contains(BOOKMARK) != true,
isTodoLayout = obj?.layout == ObjectType.Layout.TODO
allowChangingObjectType = obj.type.contains(BOOKMARK) != true,
isTodoLayout = obj.layout == ObjectType.Layout.TODO
)
}
@ -2124,44 +2118,62 @@ class DefaultBlockViewRenderer @Inject constructor(
private suspend fun mapFeaturedRelations(
ctx: Id,
keys: List<Key>,
currentObject: ObjectWrapper.Basic,
details: ObjectViewDetails,
fieldParser: FieldParser,
storeOfObjectTypes: StoreOfObjectTypes
): List<ObjectRelationView> = keys.mapNotNull { key ->
when (key) {
Relations.DESCRIPTION -> null
Relations.TYPE -> {
val objectTypeId = details.getObject(ctx)?.getProperType()
if (objectTypeId != null) {
details.objectTypeRelation(
relationKey = key,
isFeatured = true,
objectTypeId = objectTypeId
)
} else {
null
): List<ObjectRelationView> {
val objectFeaturedPropertiesKeys = currentObject.featuredRelations
val featuredProperties = if (objectFeaturedPropertiesKeys.isNotEmpty()) {
objectFeaturedPropertiesKeys.mapNotNull { key ->
storeOfRelations.getByKey(key)
}
.sortedByDescending { it.key == Relations.TYPE }
} else {
currentObject.getFeaturedPropertiesIds(
storeOfRelations = storeOfRelations,
storeOfObjectTypes = storeOfObjectTypes,
fieldParser = fieldParser
).mapNotNull { id ->
storeOfRelations.getById(id = id)
}
}
return featuredProperties.mapNotNull { property ->
when (property.key) {
Relations.DESCRIPTION -> null
Relations.TYPE -> {
val objectTypeId = details.getObject(ctx)?.getProperType()
if (objectTypeId != null) {
details.objectTypeRelation(
relationKey = property.key,
isFeatured = true,
objectTypeId = objectTypeId
)
} else {
null
}
}
Relations.BACKLINKS, Relations.LINKS -> {
details.linksFeaturedRelation(
relations = storeOfRelations.getAll(),
ctx = ctx,
relationKey = property.key,
isFeatured = true
)
}
else -> {
val values = details.getObject(ctx)?.map.orEmpty()
property.view(
details = details,
values = values,
urlBuilder = urlBuilder,
isFeatured = true,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
)
}
}
Relations.BACKLINKS, Relations.LINKS -> {
details.linksFeaturedRelation(
relations = storeOfRelations.getAll(),
ctx = ctx,
relationKey = key,
isFeatured = true
)
}
else -> {
val relation = storeOfRelations.getByKey(key)
val values = details.getObject(ctx)?.map.orEmpty()
relation?.view(
details = details,
values = values,
urlBuilder = urlBuilder,
isFeatured = true,
fieldParser = fieldParser,
storeOfObjectTypes = storeOfObjectTypes
)
}
}
}

View file

@ -4,6 +4,7 @@ import com.anytypeio.anytype.core_models.DVViewerRelation
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.MAX_SNIPPET_SIZE
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.RelationFormat
import com.anytypeio.anytype.core_models.Relations
@ -11,6 +12,8 @@ import com.anytypeio.anytype.core_utils.ext.typeOf
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.objects.getTypeOfObject
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.presentation.number.NumberParser
import com.anytypeio.anytype.presentation.relations.model.DefaultObjectRelationValueView
@ -367,4 +370,41 @@ private fun updateObjectIcon(obj: ObjectView): ObjectView {
is ObjectView.Default -> obj.copy(icon = ObjectIcon.None)
is ObjectView.Deleted -> obj
}
}
/**
* Retrieves the list of featured header property IDs for the current [ObjectWrapper.Basic] object.
*
* All objects have an associated type which can be obtained from [StoreOfObjectTypes]. In the case that the object's
* type is a Template (i.e. its unique key equals [ObjectTypeIds.TEMPLATE]), the target object type is resolved using
* the [targetObjectType] property. Once the effective object type is determined, the function uses [FieldParser] to
* obtain parsed properties and returns the header IDs.
*
* @param storeOfObjectTypes The store that provides object types.
* @param storeOfRelations The store that provides relations between objects.
* @param fieldParser The parser used to extract parsed properties from an object.
* @return A list of header property IDs, or an empty list if the necessary object type is not found.
*/
suspend fun ObjectWrapper.Basic.getFeaturedPropertiesIds(
storeOfObjectTypes: StoreOfObjectTypes,
storeOfRelations: StoreOfRelations,
fieldParser: FieldParser,
): List<Id> {
// Retrieve the object's current type.
val currentObjType = storeOfObjectTypes.getTypeOfObject(this) ?: return emptyList()
// Determine the effective object type. If the type is TEMPLATE, use the target object type.
val effectiveType = if (currentObjType.uniqueKey == ObjectTypeIds.TEMPLATE) {
this.targetObjectType?.let { storeOfObjectTypes.get(it) } ?: return emptyList()
} else {
currentObjType
}
// Parse the object's properties using the effective type.
val parsedProperties = fieldParser.getObjectParsedProperties(
objectType = effectiveType,
objPropertiesKeys = this.map.keys.toList(),
storeOfRelations = storeOfRelations
)
return parsedProperties.header.map { it.id }
}

View file

@ -114,10 +114,10 @@ class RelationListViewModel(
return emptyList()
}
val parsedFields = fieldParser.getObjectParsedFields(
val parsedFields = fieldParser.getObjectParsedProperties(
objectType = objType,
storeOfRelations = storeOfRelations,
objFieldKeys = details.getObject(ctx)?.map?.keys?.toList().orEmpty()
objPropertiesKeys = details.getObject(ctx)?.map?.keys?.toList().orEmpty()
)
val headerFields = parsedFields.header.mapNotNull {

View file

@ -11,6 +11,9 @@ import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.core_models.ObjectViewDetails
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.render.BlockViewRenderer
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
@ -21,6 +24,9 @@ import timber.log.Timber
class TemplateBlankViewModel(
private val renderer: DefaultBlockViewRenderer,
private val storeOfRelations: StoreOfRelations,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val fieldParser: FieldParser
) : BaseViewModel(), BlockViewRenderer by renderer {
val state = MutableStateFlow<List<BlockView>>(emptyList())
@ -69,14 +75,12 @@ class TemplateBlankViewModel(
children = listOf(headerBlock.id),
fields = Block.Fields.empty(),
)
val featuredRelations = listOf(Relations.TYPE)
val page = listOf(rootBlock, headerBlock, blockTitle, featuredRelationsBlock)
val objectDetails = mapOf(
DEFAULT_TEMPLATE_ID_BLANK to mapOf(
Relations.ID to DEFAULT_TEMPLATE_ID_BLANK,
Relations.LAYOUT to layout,
Relations.TYPE to typeId,
Relations.FEATURED_RELATIONS to featuredRelations,
Relations.IS_DELETED to false
)
)
@ -95,6 +99,16 @@ class TemplateBlankViewModel(
details = mapOf(DEFAULT_TEMPLATE_ID_BLANK to objectDetails, typeId to typeDetails))
viewModelScope.launch {
val objType = storeOfObjectTypes.get(typeId)
val featuredPropertiesIds = if (objType != null) {
fieldParser.getObjectParsedProperties(
objectType = objType,
objPropertiesKeys = listOf(),
storeOfRelations = storeOfRelations
).header.map { it.id }
} else {
emptyList()
}
state.value = page.asMap().render(
context = DEFAULT_TEMPLATE_ID_BLANK,
mode = Editor.Mode.Read,

View file

@ -2,17 +2,26 @@ package com.anytypeio.anytype.presentation.templates
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import javax.inject.Inject
class TemplateBlankViewModelFactory @Inject constructor(
private val renderer: DefaultBlockViewRenderer,
private val storeOfRelations: StoreOfRelations,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val fieldParser: FieldParser
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return TemplateBlankViewModel(
renderer = renderer
renderer = renderer,
storeOfRelations = storeOfRelations,
storeOfObjectTypes = storeOfObjectTypes,
fieldParser = fieldParser
) as T
}
}

View file

@ -3,6 +3,8 @@ package com.anytypeio.anytype.presentation.editor
import android.os.Build
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.Struct
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.presentation.editor.editor.BlockDimensions
import com.anytypeio.anytype.presentation.editor.editor.ViewState
@ -141,7 +143,13 @@ class BlockReadModeTest : EditorViewModelTest() {
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
context = root,
details = mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
)
)
)
)
)

View file

@ -127,7 +127,11 @@ class DataViewBlockTargetObjectSetTest : EditorPresentationTestSetup() {
Relations.SET_OF to listOf("")
)
val detailsList = ObjectViewDetails(details = mapOf(targetObjectId to objectDetails))
val detailsList = ObjectViewDetails(details = mapOf(targetObjectId to objectDetails,
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
)))
stubOpenDocument(document = listOf(page, header, title, block, dv), details = detailsList)
stubInterceptEvents(params = params)
@ -190,7 +194,15 @@ class DataViewBlockTargetObjectSetTest : EditorPresentationTestSetup() {
Relations.SET_OF to emptyList<String>()
)
val detailsList = ObjectViewDetails(details = mapOf(targetObjectId to objectDetails))
val detailsList = ObjectViewDetails(
details = mapOf(
targetObjectId to objectDetails,
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
)
)
)
stubOpenDocument(document = listOf(page, header, title, block, dv), details = detailsList)
stubInterceptEvents(params = params)
@ -315,7 +327,15 @@ class DataViewBlockTargetObjectSetTest : EditorPresentationTestSetup() {
Relations.LAYOUT to ObjectType.Layout.COLLECTION.code.toDouble()
)
val detailsList = ObjectViewDetails(details = mapOf(targetObjectId to objectDetails))
val detailsList = ObjectViewDetails(
details = mapOf(
targetObjectId to objectDetails,
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
)
)
)
stubOpenDocument(document = listOf(page, header, title, block, dv), details = detailsList)
stubInterceptEvents(params = params)
@ -424,7 +444,6 @@ class DataViewBlockTargetObjectSetTest : EditorPresentationTestSetup() {
sorts = emptyList()
)
val dv = StubDataViewBlock(viewers = listOf(emptyViewer))
val targetObjectId = MockDataFactory.randomUuid()
val page = Block(
id = root,
fields = Block.Fields.empty(),
@ -439,7 +458,7 @@ class DataViewBlockTargetObjectSetTest : EditorPresentationTestSetup() {
Event.Command.DataView.SetTargetObjectId(
context = root,
dv = dv.id,
targetObjectId = targetObjectId
targetObjectId = root
)
)
)
@ -449,10 +468,11 @@ class DataViewBlockTargetObjectSetTest : EditorPresentationTestSetup() {
document = listOf(page, header, title, block, dv),
details = ObjectViewDetails(
details = mapOf(
targetObjectId to mapOf(
Relations.ID to targetObjectId,
Relations.UNIQUE_KEY to targetObjectId,
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble()
root to mapOf(
Relations.ID to root,
Relations.UNIQUE_KEY to root,
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.TYPE to listOf(objType.id)
)
)
)

View file

@ -37,7 +37,6 @@ import com.anytypeio.anytype.presentation.MockBlockFactory.link
import com.anytypeio.anytype.presentation.MockTypicalDocumentFactory
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.core_models.ObjectViewDetails
import com.anytypeio.anytype.core_models.Struct
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.Markup.Companion.NON_EXISTENT_OBJECT_MENTION_NAME
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
@ -51,7 +50,6 @@ import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.util.TXT
import com.anytypeio.anytype.presentation.widgets.collection.ResourceProvider
import com.anytypeio.anytype.test_utils.MockDataFactory
import kotlin.random.Random
import kotlin.test.assertEquals
import kotlinx.coroutines.runBlocking
import net.lachlanmckee.timberjunit.TimberTestRule
@ -65,31 +63,6 @@ import org.mockito.kotlin.stub
class DefaultBlockViewRendererTest {
class BlockViewRenderWrapper(
private val blocks: Map<Id, List<Block>>,
private val renderer: BlockViewRenderer,
private val restrictions: List<ObjectRestriction> = emptyList()
) : BlockViewRenderer by renderer {
suspend fun render(
root: Block,
anchor: Id,
focus: Editor.Focus,
indent: Int,
details: ObjectViewDetails,
schema: NestedDecorationData = emptyList()
): List<BlockView> = blocks.render(
context = root.id,
root = root,
focus = focus,
anchor = anchor,
indent = indent,
details = details,
restrictions = restrictions,
selection = emptySet(),
parentScheme = schema
)
}
@get:Rule
val timberTestRule: TimberTestRule = TimberTestRule.builder()
.minPriority(Log.DEBUG)
@ -119,7 +92,7 @@ class DefaultBlockViewRendererTest {
private lateinit var wrapper: BlockViewRenderWrapper
private var storeOfRelations: StoreOfRelations = DefaultStoreOfRelations()
val storeOfRelations = DefaultStoreOfRelations()
private val storeOfObjectTypes = DefaultStoreOfObjectTypes()
@ -132,6 +105,32 @@ class DefaultBlockViewRendererTest {
@Mock
lateinit var stringResourceProvider: StringResourceProvider
class BlockViewRenderWrapper(
private val blocks: Map<Id, List<Block>>,
private val renderer: BlockViewRenderer,
private val restrictions: List<ObjectRestriction> = emptyList(),
private val storeOfRelations: StoreOfRelations = DefaultStoreOfRelations()
) : BlockViewRenderer by renderer {
suspend fun render(
root: Block,
anchor: Id,
focus: Editor.Focus,
indent: Int,
details: ObjectViewDetails,
schema: NestedDecorationData = emptyList()
): List<BlockView> = blocks.render(
context = root.id,
root = root,
focus = focus,
anchor = anchor,
indent = indent,
details = details,
restrictions = restrictions,
selection = emptySet(),
parentScheme = schema,
)
}
@Before
fun setup() {
MockitoAnnotations.openMocks(this)

View file

@ -105,6 +105,9 @@ import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.core_models.ObjectViewDetails
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubObjectType
import com.anytypeio.anytype.core_models.primitives.ParsedProperties
import com.anytypeio.anytype.presentation.editor.editor.BlockDimensions
import com.anytypeio.anytype.presentation.editor.editor.Command
import com.anytypeio.anytype.presentation.editor.editor.Interactor
@ -145,6 +148,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runTest
@ -395,6 +399,26 @@ open class EditorViewModelTest {
val delegator = Delegator.Default<Action>()
val objTypeUniqueKey = "objTypeUniqueKey-${MockDataFactory.randomString()}"
val objType = StubObjectType(
id = MockDataFactory.randomUuid(),
uniqueKey = objTypeUniqueKey,
)
fun showObjectEvent(blocks: List<Block>) = Event.Command.ShowObject(
root = root,
blocks = blocks,
context = root,
details = mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
objType.id to objType.map
)
)
val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
@ -425,6 +449,12 @@ open class EditorViewModelTest {
fun setup() {
MockitoAnnotations.openMocks(this)
builder = UrlBuilder(gateway)
runBlocking {
storeOfObjectTypes.merge(
types = listOf(objType)
)
}
stubParsedProperties()
stubNetworkMode()
stubObserveEvents()
stubInterceptEvents()
@ -453,6 +483,18 @@ open class EditorViewModelTest {
}
}
fun stubParsedProperties() {
fieldParser.stub {
onBlocking {
getObjectParsedProperties(
objectType = any(),
objPropertiesKeys = any(),
storeOfRelations = any()
)
} doReturn ParsedProperties()
}
}
@Test
fun `should not start observing events when view model is initialized`() {
givenViewModel()
@ -503,13 +545,7 @@ open class EditorViewModelTest {
stubOpenPage(
context = root,
events = listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
events = listOf(showObjectEvent(page))
)
stubInterceptEvents()
@ -684,13 +720,7 @@ open class EditorViewModelTest {
flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
listOf(showObjectEvent(page))
)
delay(100)
emit(
@ -779,13 +809,8 @@ open class EditorViewModelTest {
stubOpenPage(
context = root,
events = listOf(
Event.Command.ShowObject(
context = root,
blocks = listOf(smart, header, title, paragraph),
root = root
)
)
events =
listOf(showObjectEvent(listOf(smart, header, title, paragraph)))
)
stubCreateBlock(root)
@ -830,11 +855,7 @@ open class EditorViewModelTest {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
showObjectEvent(page)
)
)
delay(100)
@ -939,11 +960,7 @@ open class EditorViewModelTest {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = blocks,
context = root
)
showObjectEvent(blocks)
)
)
}
@ -1089,15 +1106,7 @@ open class EditorViewModelTest {
val events = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = blocks,
context = root
)
)
)
emit(listOf(showObjectEvent(blocks)))
}
stubObserveEvents(events)
@ -1236,15 +1245,7 @@ open class EditorViewModelTest {
val events = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = blocks,
context = root
)
)
)
emit(listOf(showObjectEvent(blocks)))
}
stubObserveEvents(events)
@ -1313,15 +1314,7 @@ open class EditorViewModelTest {
val events = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = blocks,
context = root
)
)
)
emit(listOf(showObjectEvent(blocks)))
}
stubObserveEvents(events)
@ -1412,15 +1405,7 @@ open class EditorViewModelTest {
val events = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = blocks,
context = root
)
)
)
emit(listOf(showObjectEvent(blocks)))
}
stubObserveEvents(events)
@ -1501,15 +1486,7 @@ open class EditorViewModelTest {
interceptEvents.stub {
onBlocking { build() } doReturn flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = blocks,
context = root
)
)
)
emit(listOf(showObjectEvent(blocks)))
}
}
@ -1566,15 +1543,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(1000)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent(page)))
}
stubObserveEvents(flow)
@ -1620,15 +1589,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event>> = flow {
delay(500)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = listOf(page, header, title, paragraph),
context = root
)
)
)
emit(listOf(showObjectEvent(listOf(page, header, title, paragraph))))
delay(500)
emit(
listOf(
@ -1728,15 +1689,7 @@ open class EditorViewModelTest {
val events: Flow<List<Event.Command>> = flow {
delay(1000)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubOpenPage()
@ -1798,15 +1751,7 @@ open class EditorViewModelTest {
val doc = listOf(page, header, title, child)
val events: Flow<List<Event.Command>> = flow {
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = doc,
context = root
)
)
)
emit(listOf(showObjectEvent((doc))))
}
stubOpenPage()
@ -1868,15 +1813,7 @@ open class EditorViewModelTest {
val events: Flow<List<Event.Command>> = flow {
delay(pageOpenedDelay)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = doc,
context = root
)
)
)
emit(listOf(showObjectEvent((doc))))
delay(blockDeletedEventDelay)
emit(
listOf(
@ -1995,15 +1932,7 @@ open class EditorViewModelTest {
val events: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = doc,
context = root
)
)
)
emit(listOf(showObjectEvent((doc))))
}
stubOpenPage()
@ -2054,15 +1983,7 @@ open class EditorViewModelTest {
val events: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubOpenPage()
@ -2110,15 +2031,7 @@ open class EditorViewModelTest {
val events: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubOpenPage()
@ -2162,15 +2075,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2233,23 +2138,14 @@ open class EditorViewModelTest {
)
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
stubInterceptThreadStatus()
stubOpenPage(context = root)
stubCreateBlock(root)
stubUnlinkBlocks(root)
stubInterceptThreadStatus()
givenViewModel()
vm.onStart(id = root, space = defaultSpace)
@ -2303,15 +2199,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2372,15 +2260,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2474,15 +2354,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2651,15 +2523,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2716,15 +2580,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2780,15 +2636,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2830,15 +2678,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2900,15 +2740,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -2973,15 +2805,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -3033,15 +2857,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -3132,15 +2948,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -3303,15 +3111,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -3588,7 +3388,14 @@ open class EditorViewModelTest {
private fun givenOpenDocument(
document: List<Block> = emptyList(),
details: ObjectViewDetails = ObjectViewDetails.EMPTY,
details: ObjectViewDetails = ObjectViewDetails(
details = mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
)
)
),
objectRestrictions: List<ObjectRestriction> = emptyList()
) {
openPage.stub {
@ -3957,15 +3764,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -4060,15 +3859,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -4165,15 +3956,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)
@ -4306,15 +4089,7 @@ open class EditorViewModelTest {
val flow: Flow<List<Event.Command>> = flow {
delay(100)
emit(
listOf(
Event.Command.ShowObject(
root = root,
blocks = page,
context = root
)
)
)
emit(listOf(showObjectEvent((page))))
}
stubObserveEvents(flow)

View file

@ -5,6 +5,7 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.anytypeio.anytype.core_models.ObjectViewDetails
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectTypeIds
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubObject
@ -89,6 +90,12 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
val r1 = MockTypicalDocumentFactory.relationObject("Ad")
val r2 = MockTypicalDocumentFactory.relationObject("De")
val r3 = MockTypicalDocumentFactory.relationObject("HJ")
val relationType = StubRelationObject(
id = objectTypeId,
key = Relations.TYPE,
name = "Type relation",
format = Relation.Format.LONG_TEXT
)
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
@ -129,7 +136,7 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
)
storeOfRelations.merge(
listOf(r1, r2, r3)
listOf(r1, r2, r3, relationType)
)
val vm = buildViewModel()
@ -422,6 +429,12 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
val r1 = MockTypicalDocumentFactory.relationObject("Ad")
val r2 = MockTypicalDocumentFactory.relationObject("De")
val r3 = MockTypicalDocumentFactory.relationObject("HJ")
val relationType = StubRelationObject(
id = objectTypeId,
key = Relations.TYPE,
name = "Type relation",
format = Relation.Format.LONG_TEXT
)
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
@ -461,7 +474,7 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
)
storeOfRelations.merge(
listOf(r1, r2)
listOf(r1, r2, relationType)
)
val vm = buildViewModel()
@ -545,6 +558,13 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
val objectTypeName = MockDataFactory.randomString()
val objectTypeDescription = MockDataFactory.randomString()
val relationType = StubRelationObject(
id = objectTypeId,
key = Relations.TYPE,
name = "Type relation",
format = Relation.Format.LONG_TEXT
)
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
val value3 = MockDataFactory.randomString()
@ -584,7 +604,7 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
)
storeOfRelations.merge(
listOf(r1, r2, r3)
listOf(r1, r2, r3, relationType)
)
val vm = buildViewModel()
@ -704,6 +724,13 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
Relations.FEATURED_RELATIONS to listOf(Relations.TYPE, r3.key)
)
val relationType = StubRelationObject(
id = objectTypeId,
key = Relations.TYPE,
name = "Type relation",
format = Relation.Format.LONG_TEXT
)
val customDetails = ObjectViewDetails(mapOf(root to objectFields))
stubInterceptEvents()
@ -716,7 +743,7 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
)
storeOfRelations.merge(
listOf(r1, r2, r3)
listOf(r1, r2, r3, relationType)
)
val vm = buildViewModel()
@ -836,8 +863,15 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
)
val relationType = StubRelationObject(
id = objectTypeId,
key = Relations.TYPE,
name = "Type relation",
format = Relation.Format.LONG_TEXT
)
storeOfRelations.merge(
listOf(r1, r2, r3)
listOf(r1, r2, r3, relationType)
)
storeOfObjectTypes.merge(
@ -1123,395 +1157,286 @@ class EditorFeaturedRelationsTest : EditorPresentationTestSetup() {
}
@Test
fun `should render globalName relation with proper value from global name`() = runTest {
fun `should use Featured Properties Ids from Object Type when object featured ids are empty `() =
runTest {
val title = MockTypicalDocumentFactory.title
val header = MockTypicalDocumentFactory.header
val featuredBlock = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart,
children = listOf(header.id, featuredBlock.id)
)
val doc = listOf(page, header, title, featuredBlock)
val identityRelation = StubRelationObject(
uniqueKey = Relations.IDENTITY,
key = Relations.IDENTITY,
isHidden = true
)
val globalNameRelation =
StubRelationObject(uniqueKey = Relations.GLOBAL_NAME, key = Relations.GLOBAL_NAME)
val identityValue = MockDataFactory.randomString()
val globalNameValue = "name123.any"
val objectDetails = ObjectViewDetails(
mapOf(
root to
mapOf(
Relations.ID to root,
Relations.TYPE to MockDataFactory.randomString(),
Relations.FEATURED_RELATIONS to emptyList<String>(),
Relations.IDENTITY to identityValue,
Relations.GLOBAL_NAME to globalNameValue,
Relations.LAYOUT to ObjectType.Layout.PARTICIPANT.code.toDouble()
)
val title = MockTypicalDocumentFactory.title
val header = MockTypicalDocumentFactory.header
val block = MockTypicalDocumentFactory.a
val featuredBlock = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
)
storeOfRelations.merge(
listOf(identityRelation, globalNameRelation)
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart,
children = listOf(header.id, featuredBlock.id, block.id)
)
stubGetNetworkMode()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchObjects()
stubOpenDocument(
document = doc,
details = objectDetails,
val doc = listOf(page, header, title, block, featuredBlock)
)
val property1 = StubRelationObject(
key = "property1-key",
name = "Property 1",
format = Relation.Format.SHORT_TEXT
)
val property2 = StubRelationObject(
key = "property2-key",
name = "Property 2",
format = Relation.Format.SHORT_TEXT
)
val vm = buildViewModel()
val currentObjectType = StubObjectType(
id = MockDataFactory.randomString(),
recommendedFeaturedRelations = listOf(property1.id, property2.id)
)
vm.onStart(id = root, space = defaultSpace)
val currObject = StubObject(
id = root,
objectType = currentObjectType.id,
space = defaultSpace,
extraFields = mapOf(
property1.key to "value111",
property2.key to "value222"
)
)
advanceUntilIdle()
val objectDetails = ObjectViewDetails(
mapOf(
root to currObject.map,
currentObjectType.id to currentObjectType.map
)
)
val expected = listOf(
BlockView.Title.Profile(
id = title.id,
isFocused = false,
text = title.content<Block.Content.Text>().text
),
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.Default(
id = globalNameRelation.id,
key = globalNameRelation.key,
name = globalNameRelation.name!!,
value = globalNameValue,
featured = true,
system = true,
readOnly = false,
format = Relation.Format.SHORT_TEXT
storeOfRelations.merge(
listOf(property1, property2)
)
storeOfObjectTypes.merge(
types = listOf(currentObjectType)
)
stubGetNetworkMode()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchObjects()
stubOpenDocument(
document = doc,
details = objectDetails
)
val vm = buildViewModel()
vm.onStart(id = root, space = defaultSpace)
advanceUntilIdle()
val expected = listOf(
BlockView.Title.Basic(
id = title.id,
isFocused = false,
text = title.content<Block.Content.Text>().text,
emoji = null
),
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.Default(
id = property1.id,
key = property1.key,
name = property1.name.orEmpty(),
value = "value111",
featured = true,
format = Relation.Format.SHORT_TEXT,
system = false
),
ObjectRelationView.Default(
id = property2.id,
key = property2.key,
name = property2.name.orEmpty(),
value = "value222",
featured = true,
format = Relation.Format.SHORT_TEXT,
system = false
)
)
),
BlockView.Text.Numbered(
isFocused = false,
id = block.id,
marks = emptyList(),
background = block.parseThemeBackgroundColor(),
text = block.content<Block.Content.Text>().text,
alignment = block.content<Block.Content.Text>().align?.toView(),
number = 1,
decorations = listOf(
BlockView.Decoration(
background = block.parseThemeBackgroundColor()
)
)
)
)
)
assertEquals(
expected = ViewState.Success(expected),
actual = vm.state.value
)
}
assertEquals(
expected = ViewState.Success(expected),
actual = vm.state.value
)
}
@Test
fun `should render identity relation with proper value`() = runTest {
fun `should use Featured Properties Ids from TargetObjectTypeId when object is Template and his FeatureRelations are empty`() =
runTest {
val title = MockTypicalDocumentFactory.title
val header = MockTypicalDocumentFactory.header
val featuredBlock = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart,
children = listOf(header.id, featuredBlock.id)
)
val doc = listOf(page, header, title, featuredBlock)
val identityRelation = StubRelationObject(
uniqueKey = Relations.IDENTITY,
key = Relations.IDENTITY,
isHidden = true
)
val globalNameRelation =
StubRelationObject(uniqueKey = Relations.GLOBAL_NAME, key = Relations.GLOBAL_NAME)
val identityValue = MockDataFactory.randomString()
val globalNameValue = ""
val objectDetails = ObjectViewDetails(
mapOf(
root to
mapOf(
Relations.ID to root,
Relations.TYPE to MockDataFactory.randomString(),
Relations.FEATURED_RELATIONS to listOf(Relations.IDENTITY),
Relations.IDENTITY to identityValue,
Relations.GLOBAL_NAME to globalNameValue,
Relations.LAYOUT to ObjectType.Layout.PARTICIPANT.code.toDouble()
)
val title = MockTypicalDocumentFactory.title
val header = MockTypicalDocumentFactory.header
val block = MockTypicalDocumentFactory.a
val featuredBlock = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
)
storeOfRelations.merge(
listOf(identityRelation, globalNameRelation)
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart,
children = listOf(header.id, featuredBlock.id, block.id)
)
stubGetNetworkMode()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchObjects()
stubOpenDocument(
document = doc,
details = objectDetails,
val doc = listOf(page, header, title, block, featuredBlock)
)
val property1 = StubRelationObject(
key = "property1-key",
name = "Property 1",
format = Relation.Format.SHORT_TEXT
)
val property2 = StubRelationObject(
key = "property2-key",
name = "Property 2",
format = Relation.Format.SHORT_TEXT
)
val vm = buildViewModel()
val property3 = StubRelationObject(
key = "property3-key",
name = "Property 3",
format = Relation.Format.SHORT_TEXT
)
val property4 = StubRelationObject(
key = "property4-key",
name = "Property 4",
format = Relation.Format.SHORT_TEXT
)
vm.onStart(id = root, space = defaultSpace)
val templateObjType = StubObjectType(
id = MockDataFactory.randomString(),
uniqueKey = ObjectTypeIds.TEMPLATE,
layout = ObjectType.Layout.OBJECT_TYPE.code.toDouble(),
recommendedFeaturedRelations = listOf(property1.id, property2.id)
)
advanceUntilIdle()
val targetObjectType = StubObjectType(
id = MockDataFactory.randomString(),
layout = ObjectType.Layout.BASIC.code.toDouble(),
recommendedFeaturedRelations = listOf(property3.id, property4.id)
)
val expected = listOf(
BlockView.Title.Profile(
id = title.id,
isFocused = false,
text = title.content<Block.Content.Text>().text
),
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.Default(
id = identityRelation.id,
key = identityRelation.key,
name = identityRelation.name!!,
value = identityValue,
featured = true,
system = true,
readOnly = false,
format = Relation.Format.SHORT_TEXT
val currObject = StubObject(
id = root,
objectType = templateObjType.id,
space = defaultSpace,
extraFields = mapOf(
property1.key to "value111",
property2.key to "value222",
property3.key to "value333",
property4.key to "value444",
Relations.TARGET_OBJECT_TYPE to targetObjectType.id
)
)
val objectDetails = ObjectViewDetails(
mapOf(
root to currObject.map,
templateObjType.id to templateObjType.map
)
)
storeOfRelations.merge(
listOf(property1, property2, property3, property4)
)
storeOfObjectTypes.merge(
types = listOf(templateObjType, targetObjectType)
)
stubGetNetworkMode()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchObjects()
stubOpenDocument(
document = doc,
details = objectDetails
)
val vm = buildViewModel()
vm.onStart(id = root, space = defaultSpace)
advanceUntilIdle()
val expected = listOf(
BlockView.Title.Basic(
id = title.id,
isFocused = false,
text = title.content<Block.Content.Text>().text,
emoji = null
),
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.Default(
id = property3.id,
key = property3.key,
name = property3.name.orEmpty(),
value = "value333",
featured = true,
format = Relation.Format.SHORT_TEXT,
system = false
),
ObjectRelationView.Default(
id = property4.id,
key = property4.key,
name = property4.name.orEmpty(),
value = "value444",
featured = true,
format = Relation.Format.SHORT_TEXT,
system = false
)
)
),
BlockView.Text.Numbered(
isFocused = false,
id = block.id,
marks = emptyList(),
background = block.parseThemeBackgroundColor(),
text = block.content<Block.Content.Text>().text,
alignment = block.content<Block.Content.Text>().align?.toView(),
number = 1,
decorations = listOf(
BlockView.Decoration(
background = block.parseThemeBackgroundColor()
)
)
)
)
)
assertEquals(
expected = ViewState.Success(expected),
actual = vm.state.value
)
}
@Test
fun `should add globalName relation to featured when is present in details`() = runTest {
val title = MockTypicalDocumentFactory.title
val header = MockTypicalDocumentFactory.header
val featuredBlock = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart,
children = listOf(header.id, featuredBlock.id)
)
val doc = listOf(page, header, title, featuredBlock)
val globalNameRelation =
StubRelationObject(uniqueKey = Relations.GLOBAL_NAME, key = Relations.GLOBAL_NAME)
val globalNameValue = "name123.any"
val someRelationKey = MockDataFactory.randomString()
val someRelation = StubRelationObject(uniqueKey = someRelationKey, key = someRelationKey)
val someRelationValue = "Some relation Value"
val objectDetails = ObjectViewDetails(
mapOf(
root to
mapOf(
Relations.ID to root,
Relations.TYPE to MockDataFactory.randomString(),
Relations.FEATURED_RELATIONS to listOf(someRelationKey),
Relations.GLOBAL_NAME to globalNameValue,
someRelationKey to someRelationValue,
Relations.LAYOUT to ObjectType.Layout.PARTICIPANT.code.toDouble()
)
assertEquals(
expected = ViewState.Success(expected),
actual = vm.state.value
)
)
storeOfRelations.merge(
listOf(globalNameRelation, someRelation)
)
stubGetNetworkMode()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchObjects()
stubOpenDocument(
document = doc,
details = objectDetails,
)
val vm = buildViewModel()
vm.onStart(id = root, space = defaultSpace)
advanceUntilIdle()
val expected = listOf(
BlockView.Title.Profile(
id = title.id,
isFocused = false,
text = title.content<Block.Content.Text>().text
),
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.Default(
id = globalNameRelation.id,
key = globalNameRelation.key,
name = globalNameRelation.name!!,
value = globalNameValue,
featured = true,
system = true,
readOnly = false,
format = Relation.Format.SHORT_TEXT
),
ObjectRelationView.Default(
id = someRelation.id,
key = someRelation.key,
name = someRelation.name!!,
value = someRelationValue,
featured = true,
system = false,
readOnly = false,
format = Relation.Format.SHORT_TEXT
)
)
)
)
assertEquals(
expected = ViewState.Success(expected),
actual = vm.state.value
)
}
@Test
fun `should add identity relation to featured when identity is presented`() = runTest {
val title = MockTypicalDocumentFactory.title
val header = MockTypicalDocumentFactory.header
val featuredBlock = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart,
children = listOf(header.id, featuredBlock.id)
)
val doc = listOf(page, header, title, featuredBlock)
val globalNameRelation =
StubRelationObject(uniqueKey = Relations.GLOBAL_NAME, key = Relations.GLOBAL_NAME)
val globalNameValue = "name123.any"
val identityRelation =
StubRelationObject(uniqueKey = Relations.IDENTITY, key = Relations.IDENTITY)
val identityValue = MockDataFactory.randomString()
val someRelationKey = MockDataFactory.randomString()
val someRelation = StubRelationObject(uniqueKey = someRelationKey, key = someRelationKey)
val someRelationValue = "Some relation Value"
val objectDetails = ObjectViewDetails(
mapOf(
root to
mapOf(
Relations.ID to root,
Relations.TYPE to MockDataFactory.randomString(),
Relations.FEATURED_RELATIONS to listOf(someRelationKey),
Relations.IDENTITY to identityValue,
someRelationKey to someRelationValue,
Relations.LAYOUT to ObjectType.Layout.PARTICIPANT.code.toDouble()
)
)
)
storeOfRelations.merge(
listOf(globalNameRelation, someRelation, identityRelation)
)
stubGetNetworkMode()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchObjects()
stubOpenDocument(
document = doc,
details = objectDetails,
)
val vm = buildViewModel()
vm.onStart(id = root, space = defaultSpace)
advanceUntilIdle()
val expected = listOf(
BlockView.Title.Profile(
id = title.id,
isFocused = false,
text = title.content<Block.Content.Text>().text
),
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.Default(
id = identityRelation.id,
key = identityRelation.key,
name = identityRelation.name!!,
value = identityValue,
featured = true,
system = true,
readOnly = false,
format = Relation.Format.SHORT_TEXT
),
ObjectRelationView.Default(
id = someRelation.id,
key = someRelation.key,
name = someRelation.name!!,
value = someRelationValue,
featured = true,
system = false,
readOnly = false,
format = Relation.Format.SHORT_TEXT
)
)
)
)
assertEquals(
expected = ViewState.Success(expected),
actual = vm.state.value
)
}
}
}

View file

@ -282,6 +282,10 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
document = page,
details = ObjectViewDetails(
mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
target to
mapOf(
Relations.ID to target,
@ -394,6 +398,10 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
document = page,
details = ObjectViewDetails(
mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
target to
mapOf(
Relations.ID to target,
@ -507,6 +515,10 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
document = page,
details = ObjectViewDetails(
mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
bookmarkObjectId to
mapOf(
Relations.ID to root,
@ -615,6 +627,10 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
document = page,
details = ObjectViewDetails(
mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
targetObjectId to
mapOf(
Relations.ID to targetObjectId,
@ -716,6 +732,10 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
document = page,
details = ObjectViewDetails(
mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
targetObjectId to
mapOf(
Relations.ID to targetObjectId,

View file

@ -171,7 +171,11 @@ class EditorMentionTest : EditorPresentationTestSetup() {
Relations.ID to mentionHash,
Relations.LAYOUT to Layout.BASIC.code.toDouble(),
Relations.NAME to mentionText
)
),
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
)
)
)
@ -862,7 +866,11 @@ class EditorMentionTest : EditorPresentationTestSetup() {
mentionTarget to mapOf(
Relations.ID to mentionTarget,
Relations.NAME to ""
)
),
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
),
objectRestrictions = emptyList()
),
@ -1001,6 +1009,10 @@ class EditorMentionTest : EditorPresentationTestSetup() {
val detailsAmend = mapOf(
mentionTarget to fieldsUpdated1,
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
)
val document = listOf(page, header, title, a)
@ -1286,6 +1298,10 @@ class EditorMentionTest : EditorPresentationTestSetup() {
val detailsAmend = mapOf(
mentionTarget to fieldsUpdated1,
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
)
val document = listOf(page, header, title, a)
@ -1443,6 +1459,10 @@ class EditorMentionTest : EditorPresentationTestSetup() {
val detailsAmend = mapOf(
mentionTarget to fieldsUpdated1,
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
),
)
val document = listOf(page, header, title, a)

View file

@ -6,8 +6,8 @@ import app.cash.turbine.test
import com.anytypeio.anytype.core_models.ObjectViewDetails
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubObjectType
import com.anytypeio.anytype.core_models.StubRelationObject
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
@ -86,20 +86,9 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
val doc = listOf(page, header, featuredBlock)
val objectTypeId = "objectTypeId"
val objectTypeName = "objectTypeName"
val objectTypeDescription = "objectTypeDesc"
val r1 = StubRelationObject(name = "Ad")
val r1 = StubRelationObject(name = "Ad", format = Relation.Format.SHORT_TEXT)
val r2 = StubRelationObject(name = "De")
val r3 = StubRelationObject(name = "HJ")
val objectRelations = listOf(r1, r2, r3)
val objectType = StubObjectType(
id = objectTypeId,
name = "Object Type"
)
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
@ -110,25 +99,20 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
r1.key to value1,
r2.key to value2,
r3.key to value3,
Relations.TYPE to objectType.id,
Relations.FEATURED_RELATIONS to listOf(Relations.TYPE),
Relations.LAYOUT to ObjectType.Layout.NOTE.code.toDouble()
Relations.TYPE to listOf(objType.id),
Relations.LAYOUT to ObjectType.Layout.NOTE.code.toDouble(),
Relations.FEATURED_RELATIONS to listOf(r1.key)
)
val objectTypeFields =
mapOf(
Relations.ID to objectTypeId,
Relations.UNIQUE_KEY to objectType.uniqueKey,
Relations.NAME to objectTypeName,
Relations.DESCRIPTION to objectTypeDescription
)
val customDetails = ObjectViewDetails(
mapOf(
root to objectFields,
objectTypeId to objectTypeFields
objType.id to objType.map
)
)
storeOfRelations.merge(listOf(r1, r2, r3))
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchObjects()
@ -148,14 +132,14 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.ObjectType.Base(
id = objectType.id,
key = Relations.TYPE,
name = objectTypeName,
value = null,
ObjectRelationView.Default(
id = r1.id,
key = r1.key,
name = r1.name.orEmpty(),
value = value1,
featured = true,
type = objectTypeId,
system = true
system = false,
format = Relation.Format.SHORT_TEXT
)
)
)
@ -202,22 +186,12 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
val doc = listOf(page, header, featuredBlock)
val objectTypeId = "objectTypeId"
val objectTypeName = "objectTypeName"
val objectTypeDescription = "objectTypeDesc"
val r1 = StubRelationObject(name = "Ad")
val r1 = StubRelationObject(name = "Ad", format = Relation.Format.NUMBER)
val r2 = StubRelationObject(name = "De")
val r3 = StubRelationObject(name = "HJ")
val objectRelations = listOf(r1, r2, r3)
val relationObjectType = StubObjectType(
id = objectTypeId,
name = "Object Type"
)
val value1 = MockDataFactory.randomString()
val value1 = 123.0
val value2 = MockDataFactory.randomString()
val value3 = MockDataFactory.randomString()
val objectFields =
@ -226,22 +200,15 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
r1.key to value1,
r2.key to value2,
r3.key to value3,
Relations.TYPE to objectTypeId,
Relations.FEATURED_RELATIONS to listOf(Relations.TYPE),
Relations.TYPE to objType.id,
Relations.FEATURED_RELATIONS to listOf(r1.key),
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble()
)
val objectTypeFields =
mapOf(
Relations.ID to objectTypeId,
Relations.UNIQUE_KEY to MockDataFactory.randomString(),
Relations.NAME to objectTypeName,
Relations.DESCRIPTION to objectTypeDescription
)
val customDetails = ObjectViewDetails(
mapOf(
root to objectFields,
objectTypeId to objectTypeFields
objType.id to objType.map
)
)
@ -265,20 +232,24 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
ObjectRelationView.ObjectType.Base(
id = objectTypeId,
key = Relations.TYPE,
name = objectTypeName,
value = null,
ObjectRelationView.Default(
id = r1.id,
key = r1.key,
name = r1.name.orEmpty(),
value = "123",
featured = true,
type = objectTypeId,
system = true
system = false,
format = Relation.Format.NUMBER
)
)
)
)
vm.state.test().assertValue(ViewState.Success(expected))
assertEquals(
expected = ViewState.Success(expected),
actual = vm.state.value
)
//vm.state.test().assertValue(ViewState.Success(expected))
vm.footers.test {
val result = awaitItem()

View file

@ -8,8 +8,11 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.NetworkModeConfig
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.Response
import com.anytypeio.anytype.core_models.StubObjectType
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
import com.anytypeio.anytype.core_models.primitives.ParsedProperties
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.primitives.TypeId
import com.anytypeio.anytype.core_models.primitives.TypeKey
@ -127,6 +130,7 @@ import com.anytypeio.anytype.test_utils.MockDataFactory
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.kotlin.any
@ -395,8 +399,21 @@ open class EditorPresentationTestSetup {
var permissions: UserPermissionProvider = UserPermissionProviderStub()
val objTypeUniqueKey = "objTypeUniqueKey-${MockDataFactory.randomString()}"
val objType = StubObjectType(
id = MockDataFactory.randomUuid(),
uniqueKey = objTypeUniqueKey,
)
open fun buildViewModel(urlBuilder: UrlBuilder = builder): EditorViewModel {
runBlocking {
storeOfObjectTypes.merge(
types = listOf(objType)
)
}
val storage = Editor.Storage()
val proxies = Editor.Proxer()
val memory = Editor.Memory(
@ -527,6 +544,18 @@ open class EditorPresentationTestSetup {
)
}
fun stubParsedProperties() {
fieldParser.stub {
onBlocking {
getObjectParsedProperties(
objectType = any(),
objPropertiesKeys = any(),
storeOfRelations = any()
)
} doReturn ParsedProperties()
}
}
fun stubGetNetworkMode() {
getNetworkMode.stub {
onBlocking { run(Unit) } doReturn NetworkModeConfig()
@ -535,7 +564,14 @@ open class EditorPresentationTestSetup {
fun stubOpenDocument(
document: List<Block> = emptyList(),
details: ObjectViewDetails = ObjectViewDetails.EMPTY,
details: ObjectViewDetails = ObjectViewDetails(
details = mapOf(
root to mapOf(
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
)
)
),
objectRestrictions: List<ObjectRestriction> = emptyList(),
spaceId: SpaceId = SpaceId(defaultSpace)
) {

View file

@ -161,11 +161,16 @@ class EditorRelationBlockTest : EditorPresentationTestSetup() {
val title = MockTypicalDocumentFactory.title
val header = MockTypicalDocumentFactory.header
val block = MockTypicalDocumentFactory.a
val missedProperty = StubRelationObject(
format = Relation.Format.LONG_TEXT
)
val relationBlock = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.RelationBlock(key = MockDataFactory.randomString())
content = Block.Content.RelationBlock(key = missedProperty.key)
)
val page = Block(
@ -177,18 +182,9 @@ class EditorRelationBlockTest : EditorPresentationTestSetup() {
val doc = listOf(page, header, title, block, relationBlock)
val objectTypeId = MockDataFactory.randomString()
val objectTypeName = MockDataFactory.randomString()
val objectTypeDescription = MockDataFactory.randomString()
val r1 = MockTypicalDocumentFactory.relationObject("Ad")
val r2 = MockTypicalDocumentFactory.relationObject("De")
val r3 = MockTypicalDocumentFactory.relationObject("HJ")
val relationObjectType = StubRelationObject(
key = Block.Fields.TYPE_KEY,
name = "Object Type",
format = Relation.Format.OBJECT
)
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
@ -198,19 +194,14 @@ class EditorRelationBlockTest : EditorPresentationTestSetup() {
r1.key to value1,
r2.key to value2,
r3.key to value3,
relationObjectType.key to objectTypeId
)
val objectTypeFields =
mapOf(
Relations.NAME to objectTypeName,
Relations.DESCRIPTION to objectTypeDescription
missedProperty.key to "Some Text",
Relations.ID to root,
Relations.TYPE to listOf(objType.id)
)
val customDetails = ObjectViewDetails(
mapOf(
root to objectFields,
objectTypeId to objectTypeFields
)
)
@ -223,7 +214,7 @@ class EditorRelationBlockTest : EditorPresentationTestSetup() {
)
storeOfRelations.merge(
listOf(r1, r2, r3, relationObjectType)
listOf(r1, r2, r3)
)
val vm = buildViewModel()

View file

@ -6,6 +6,7 @@ import com.anytypeio.anytype.core_models.ObjectViewDetails
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubRelationObject
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
@ -188,7 +189,13 @@ class EditorSlashWidgetRelationsTest: EditorPresentationTestSetup() {
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
val value3 = MockDataFactory.randomString()
val fields = mapOf(r1.key to value1, r2.key to value2, r3.key to value3)
val fields = mapOf(
r1.key to value1,
r2.key to value2,
r3.key to value3,
Relations.ID to root,
Relations.TYPE to listOf(objType.id),
)
val customDetails = ObjectViewDetails(mapOf(root to fields))
stubInterceptEvents()
@ -311,7 +318,8 @@ class EditorSlashWidgetRelationsTest: EditorPresentationTestSetup() {
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
val value3 = MockDataFactory.randomString()
val fields = mapOf(r1.key to value1, r2.key to value2, r3.key to value3)
val fields = mapOf(r1.key to value1, r2.key to value2, r3.key to value3, Relations.ID to root,
Relations.TYPE to listOf(objType.id),)
val customDetails = ObjectViewDetails(details = mapOf(root to fields))
stubInterceptEvents()
@ -503,7 +511,8 @@ class EditorSlashWidgetRelationsTest: EditorPresentationTestSetup() {
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
val value3 = MockDataFactory.randomString()
val fields = mapOf(r1.key to value1, r2.key to value2, r3.key to value3)
val fields = mapOf(r1.key to value1, r2.key to value2, r3.key to value3, Relations.ID to root,
Relations.TYPE to listOf(objType.id),)
val customDetails = ObjectViewDetails(mapOf(root to fields))
stubInterceptEvents()

View file

@ -25,6 +25,7 @@ import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.core_models.ObjectViewDetails
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.render.BlockViewRenderer
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer