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

DROID-496 Sets | Enhancement | Creating object in sets aggregated by relations (#2731)

This commit is contained in:
Evgenii Kozlov 2022-12-01 14:13:41 +03:00 committed by GitHub
parent 64e5e8c6dd
commit ef6eeac53c
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 461 additions and 192 deletions

View file

@ -20,11 +20,13 @@ import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.SetDataViewSource
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
import com.anytypeio.anytype.domain.dataview.interactor.UpdateDataViewViewer
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DefaultObjectStore
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
@ -88,6 +90,9 @@ abstract class TestObjectSetSetup {
@Mock
lateinit var auth: AuthRepository
@Mock
lateinit var userSettingsRepository: UserSettingsRepository
@Mock
lateinit var gateway: Gateway
@Mock
@ -105,6 +110,7 @@ abstract class TestObjectSetSetup {
lateinit var createNewObject: CreateNewObject
private lateinit var getTemplates: GetTemplates
private lateinit var getDefaultEditorType: GetDefaultEditorType
private val session = ObjectSetSession()
private val reducer = ObjectSetReducer()
@ -147,7 +153,15 @@ abstract class TestObjectSetSetup {
setDataViewSource = SetDataViewSource(repo)
updateText = UpdateText(repo)
openObjectSet = OpenObjectSet(repo, auth)
createDataViewObject = CreateDataViewObject(repo)
getDefaultEditorType = GetDefaultEditorType(
userSettingsRepository = userSettingsRepository
)
createDataViewObject = CreateDataViewObject(
getTemplates = getTemplates,
repo = repo,
storeOfRelations = storeOfRelations,
getDefaultEditorType = getDefaultEditorType
)
setObjectDetails = UpdateDetail(repo)
updateDataViewViewer = UpdateDataViewViewer(repo)
interceptThreadStatus = InterceptThreadStatus(channel = threadStatusChannel)
@ -192,7 +206,6 @@ abstract class TestObjectSetSetup {
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
delegator = delegator,
getTemplates = getTemplates,
createNewObject = createNewObject,
setDataViewSource = setDataViewSource,
cancelSearchSubscription = cancelSearchSubscription,

View file

@ -151,7 +151,6 @@ object ObjectSetModule {
analytics: Analytics,
downloadUnsplashImage: DownloadUnsplashImage,
setDocCoverImage: SetDocCoverImage,
getTemplates: GetTemplates,
dataViewSubscriptionContainer: DataViewSubscriptionContainer,
cancelSearchSubscription: CancelSearchSubscription,
setDataViewSource: SetDataViewSource,
@ -176,7 +175,6 @@ object ObjectSetModule {
analytics = analytics,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
getTemplates = getTemplates,
createNewObject = createNewObject,
dataViewSubscriptionContainer = dataViewSubscriptionContainer,
cancelSearchSubscription = cancelSearchSubscription,
@ -240,8 +238,16 @@ object ObjectSetModule {
@Provides
@PerScreen
fun provideCreateDataViewRecordUseCase(
repo: BlockRepository
): CreateDataViewObject = CreateDataViewObject(repo = repo)
repo: BlockRepository,
storeOfRelations: StoreOfRelations,
getDefaultEditorType: GetDefaultEditorType,
getTemplates: GetTemplates
): CreateDataViewObject = CreateDataViewObject(
repo = repo,
getDefaultEditorType = getDefaultEditorType,
getTemplates = getTemplates,
storeOfRelations = storeOfRelations
)
@JvmStatic
@Provides

View file

@ -194,7 +194,6 @@ sealed class ObjectWrapper {
val isReadOnly: Boolean? by default
val isArchived: Boolean? by default
val isDeleted: Boolean? by default
val isReadonly: Boolean? by default
val isReadonlyValue: Boolean = relationReadonlyValue ?: false
val restrictions: List<ObjectRestriction>

View file

@ -318,8 +318,10 @@ class FeaturedRelationGroupWidget : ConstraintLayout {
setTextColor(context.color(R.color.text_secondary))
setTextSize(context.dimen(R.dimen.featured_relations_text_size))
setup(
name = obj.name,
icon = obj.icon
name = resources.getString(
R.string.set_by_type,
obj.name
)
)
}
is ObjectView.Deleted -> {

View file

@ -45,7 +45,10 @@ class ObjectIconTextWidget @JvmOverloads constructor(
binding.objectName.setTextColor(textColor)
}
fun setup(name: String?, icon: ObjectIcon) {
fun setup(
name: String?,
icon: ObjectIcon = ObjectIcon.None
) {
binding.objectName.text = name
binding.objectIcon.setIcon(icon)
if (icon is ObjectIcon.None) {

View file

@ -579,5 +579,6 @@
<string name="error_block_selection">Block selection error</string>
<string name="undefined">Undefined</string>
<string name="set_by_relations">Relations: %1$s</string>
<string name="set_by_type">Type: %1$s</string>
</resources>

View file

@ -385,7 +385,7 @@ class BlockDataRepository(
override suspend fun createDataViewObject(
type: Id,
template: Id?,
prefilled: Map<Id, Any>,
prefilled: Struct,
): Id = remote.createDataViewRecord(
template = template,
prefilled = prefilled,

View file

@ -119,7 +119,7 @@ interface BlockDataStore {
suspend fun createDataViewRecord(
type: Id,
template: Id?,
prefilled: Map<Id, Any>,
prefilled: Struct,
): Id
suspend fun addDataViewViewer(

View file

@ -126,7 +126,7 @@ interface BlockRemote {
suspend fun createDataViewObject(
type: Id,
template: Id?,
prefilled: Map<Id, Any>,
prefilled: Struct,
): Id
suspend fun addDataViewViewer(

View file

@ -284,7 +284,7 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
override suspend fun createDataViewRecord(
type: Id,
template: Id?,
prefilled: Map<Id, Any>,
prefilled: Struct,
): Id = remote.createDataViewObject(
template = template,
prefilled = prefilled,

View file

@ -177,7 +177,7 @@ interface BlockRepository {
suspend fun createDataViewObject(
type: Id,
template: Id?,
prefilled: Map<Id, Any>,
prefilled: Struct,
): Id
suspend fun addDataViewViewer(

View file

@ -1,32 +1,128 @@
package com.anytypeio.anytype.domain.dataview.interactor
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.ObjectTypeIds
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.Struct
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
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 repo: BlockRepository,
private val getTemplates: GetTemplates,
private val getDefaultEditorType: GetDefaultEditorType,
private val storeOfRelations: StoreOfRelations
) : BaseUseCase<Id, CreateDataViewObject.Params>() {
override suspend fun run(params: Params) = safe {
repo.createDataViewObject(
template = params.template,
prefilled = params.prefilled,
type = params.type
)
when(params) {
is Params.SetByType -> {
repo.createDataViewObject(
template = resolveTemplateForNewObject(type = params.type),
prefilled = resolveSetByTypePrefilledObjectData(
filters = params.filters
),
type = params.type
)
}
is Params.SetByRelation -> {
val type = resolveDefaultObjectType()
repo.createDataViewObject(
template = resolveTemplateForNewObject(type = type),
prefilled = resolveSetByRelationPrefilledObjectData(
filters = params.filters
),
type = type
)
}
}
}
/**
* @property [type] type of the new object
* @property [template] optional template for dv record
* @property [prefilled] prefilled or pre-populated data for dv record
*/
data class Params(
val type: Id,
val template: Id?,
val prefilled: Map<Id, Any> = emptyMap()
)
private suspend fun resolveSetByTypePrefilledObjectData(filters: List<DVFilter>): Struct = buildMap {
filters.forEach { filter ->
val relation = storeOfRelations.getByKey(filter.relationKey)
if (relation != null && relation.isReadOnly == false) {
if (filter.condition == DVFilterCondition.ALL_IN || filter.condition == DVFilterCondition.IN || filter.condition == DVFilterCondition.EQUAL) {
filter.value?.let { put(filter.relationKey, it) }
}
}
}
}
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>): Struct = try {
buildMap {
filters.forEach { filter ->
val relation = storeOfRelations.getByKey(filter.relationKey)
if (relation != null && !relation.isReadonlyValue) {
if (filter.condition == DVFilterCondition.ALL_IN || filter.condition == DVFilterCondition.IN || filter.condition == DVFilterCondition.EQUAL) {
val value = filter.value
if (value != null) {
put(filter.relationKey, value)
}
} else {
when(relation.format) {
Relation.Format.LONG_TEXT,
Relation.Format.SHORT_TEXT,
Relation.Format.URL,
Relation.Format.EMAIL,
Relation.Format.PHONE,
Relation.Format.EMOJI -> {
put(filter.relationKey, EMPTY_STRING_VALUE)
}
Relation.Format.NUMBER -> {
put(filter.relationKey, null)
}
Relation.Format.CHECKBOX -> {
put(filter.relationKey, false)
}
else -> {
put(filter.relationKey, null)
}
}
}
}
}
}
} catch (e: Exception) {
emptyMap()
}
private suspend fun resolveDefaultObjectType() : Id {
return try {
getDefaultEditorType.run(Unit).type ?: ObjectTypeIds.NOTE
} catch (e: Exception) {
ObjectTypeIds.NOTE
}
}
sealed class Params {
data class SetByType(
val type: Id,
val filters: List<DVFilter>
) : Params()
data class SetByRelation(
val filters: List<DVFilter>
) : Params()
}
companion object {
const val EMPTY_STRING_VALUE = ""
}
}

View file

@ -316,7 +316,7 @@ class BlockMiddleware(
override suspend fun createDataViewObject(
type: Id,
template: Id?,
prefilled: Map<Id, Any>,
prefilled: Struct,
): Id = middleware.objectCreate(
template = template,
prefilled = prefilled,

View file

@ -916,11 +916,11 @@ class Middleware(
fun objectCreate(
type: Id,
template: Id?,
prefilled: Map<Id, Any>,
prefilled: Struct,
shouldSelectType: Boolean = false,
shouldEmptyDelete: Boolean = false
) : Id {
val details: Map<String, Any> = buildMap {
val details: Struct = buildMap {
put(Relations.TYPE, type)
putAll(prefilled)
}

View file

@ -71,8 +71,8 @@ private fun mapFeaturedRelations(
Relations.SET_OF -> {
val objectSet = ObjectWrapper.Basic(details.details[ctx]?.map.orEmpty())
val sources = mutableListOf<ObjectView>()
objectSet.setOf.forEach { objectTypeId ->
val wrapper = ObjectWrapper.Basic(details.details[objectTypeId]?.map.orEmpty())
objectSet.setOf.forEach { sourceId ->
val wrapper = ObjectWrapper.Basic(details.details[sourceId]?.map.orEmpty())
if (!wrapper.isEmpty()) {
sources.add(
wrapper.toObjectView(urlBuilder = urlBuilder)

View file

@ -10,7 +10,6 @@ import com.anytypeio.anytype.analytics.base.sendEvent
import com.anytypeio.anytype.analytics.props.Props
import com.anytypeio.anytype.core_models.DV
import com.anytypeio.anytype.core_models.DVFilter
import com.anytypeio.anytype.core_models.DVFilterCondition
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
@ -44,7 +43,6 @@ import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
@ -94,7 +92,6 @@ class ObjectSetViewModel(
private val closeBlock: CloseBlock,
private val updateDataViewViewer: UpdateDataViewViewer,
private val setObjectDetails: UpdateDetail,
private val createDataViewObject: CreateDataViewObject,
private val downloadUnsplashImage: DownloadUnsplashImage,
private val setDocCoverImage: SetDocCoverImage,
private val updateText: UpdateText,
@ -106,7 +103,7 @@ class ObjectSetViewModel(
private val coverImageHashProvider: CoverImageHashProvider,
private val session: ObjectSetSession,
private val analytics: Analytics,
private val getTemplates: GetTemplates,
private val createDataViewObject: CreateDataViewObject,
private val createNewObject: CreateNewObject,
private val dataViewSubscriptionContainer: DataViewSubscriptionContainer,
private val cancelSearchSubscription: CancelSearchSubscription,
@ -746,35 +743,41 @@ class ObjectSetViewModel(
val setObject = ObjectWrapper.Basic(
currentState.details[context]?.map ?: emptyMap()
)
val setOfTypeId = setObject.setOf.singleOrNull()
if (setOfTypeId == ObjectTypeIds.BOOKMARK) {
dispatch(
ObjectSetCommand.Modal.CreateBookmark(
ctx = context
)
)
} else if (!setOfTypeId.isNullOrEmpty()) {
viewModelScope.launch {
createDataViewObject(
CreateDataViewObject.Params(
template = resolveTemplateForNewRecord(),
prefilled = resolvePrefilledRecordData(currentState),
type = setOfTypeId
)
).process(
failure = { Timber.e(it, "Error while creating new record") },
success = { record ->
dispatch(
ObjectSetCommand.Modal.SetNameForCreatedObject(
ctx = context,
target = record
val viewer = currentState.viewerById(session.currentViewerId.value)
val sourceId = setObject.setOf.singleOrNull()
if (sourceId == null) {
toast("Unabled to define source for new object")
} else {
val sourceDetails = currentState.details[sourceId]
if (sourceDetails != null && sourceDetails.map.isNotEmpty()) {
when(sourceDetails.type.firstOrNull()) {
ObjectTypeIds.OBJECT_TYPE -> {
if (sourceId == ObjectTypeIds.BOOKMARK) {
dispatch(
ObjectSetCommand.Modal.CreateBookmark(
ctx = context
)
)
} else {
proceedWithCreatingDataViewObject(
CreateDataViewObject.Params.SetByType(
type = sourceId,
filters = viewer.filters
)
)
}
}
ObjectTypeIds.RELATION -> {
proceedWithCreatingDataViewObject(
CreateDataViewObject.Params.SetByRelation(
filters = viewer.filters
)
)
}
)
}
} else {
toast("Unabled to define source for new object")
}
} else {
toast("Unabled to define type for new object")
}
}
} else {
@ -782,32 +785,19 @@ class ObjectSetViewModel(
}
}
private fun resolvePrefilledRecordData(setOfObjects: ObjectSet): Map<Id, Any> = buildMap {
val viewer = setOfObjects.viewerById(session.currentViewerId.value)
val block = setOfObjects.dataview
val dv = block.content as DV
viewer.filters.forEach { filter ->
val relation = dv.relations.find { it.key == filter.relationKey }
if (relation != null && !relation.isReadOnly) {
if (filter.condition == DVFilterCondition.ALL_IN || filter.condition == DVFilterCondition.IN || filter.condition == DVFilterCondition.EQUAL) {
filter.value?.let { put(filter.relationKey, it) }
private fun proceedWithCreatingDataViewObject(params: CreateDataViewObject.Params) {
viewModelScope.launch {
createDataViewObject(params).process(
failure = { Timber.e(it, "Error while creating new record") },
success = { record ->
dispatch(
ObjectSetCommand.Modal.SetNameForCreatedObject(
ctx = context,
target = record
)
)
}
}
}
}
private suspend fun resolveTemplateForNewRecord(): Id? {
val obj = ObjectWrapper.Basic(reducer.state.value.details[context]?.map ?: emptyMap())
val type = obj.setOf.singleOrNull()
return if (type != null) {
val templates = try {
getTemplates.run(GetTemplates.Params(type))
} catch (e: Exception) {
emptyList()
}
templates.singleOrNull()?.id
} else {
null
)
}
}

View file

@ -19,7 +19,6 @@ import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
@ -44,7 +43,6 @@ class ObjectSetViewModelFactory(
private val urlBuilder: UrlBuilder,
private val session: ObjectSetSession,
private val analytics: Analytics,
private val getTemplates: GetTemplates,
private val createNewObject: CreateNewObject,
private val dataViewSubscriptionContainer: DataViewSubscriptionContainer,
private val cancelSearchSubscription: CancelSearchSubscription,
@ -73,7 +71,6 @@ class ObjectSetViewModelFactory(
urlBuilder = urlBuilder,
session = session,
analytics = analytics,
getTemplates = getTemplates,
createNewObject = createNewObject,
dataViewSubscriptionContainer = dataViewSubscriptionContainer,
cancelSearchSubscription = cancelSearchSubscription,

View file

@ -8,8 +8,13 @@ import com.anytypeio.anytype.core_models.DVFilterCondition
import com.anytypeio.anytype.core_models.DVFilterOperator
import com.anytypeio.anytype.core_models.DVViewer
import com.anytypeio.anytype.core_models.DVViewerRelation
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.StubDataView
import com.anytypeio.anytype.core_models.StubDataViewView
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
@ -28,25 +33,8 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
@get:Rule
val coroutineTestRule = CoroutinesTestRule()
private val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
style = Block.Content.Text.Style.TITLE,
text = MockDataFactory.randomString(),
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
private val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
private val title = StubTitle()
private val header = StubHeader(children = listOf(title.id))
@Before
fun setup() {
@ -59,27 +47,17 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
// SETUP
val viewer = DVViewer(
id = MockDataFactory.randomUuid(),
filters = emptyList(),
sorts = emptyList(),
type = Block.Content.DataView.Viewer.Type.GRID,
name = MockDataFactory.randomString(),
viewerRelations = emptyList()
val viewer = StubDataViewView(
type = Block.Content.DataView.Viewer.Type.GRID
)
val dv = Block(
val dv = StubDataView(
id = MockDataFactory.randomUuid(),
content = DV(
sources = listOf(MockDataFactory.randomString()),
relations = emptyList(),
viewers = listOf(viewer)
),
children = emptyList(),
fields = Block.Fields.empty()
views = listOf(viewer),
sources = listOf(MockDataFactory.randomString())
)
val type = MockDataFactory.randomUuid()
val givenType = MockDataFactory.randomUuid()
val newObjectId = MockDataFactory.randomUuid()
stubInterceptEvents()
@ -87,7 +65,7 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
stubInterceptThreadStatus()
stubSearchWithSubscription()
stubSubscriptionEventChannel()
stubGetTemplates(type = type)
stubGetTemplates(type = givenType)
stubOpenObjectSet(
doc = listOf(
header,
@ -97,7 +75,10 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
details = Block.Details(
mapOf(
root to Block.Fields(
map = mapOf(Relations.SET_OF to listOf(type))
map = mapOf(Relations.SET_OF to listOf(givenType))
),
givenType to Block.Fields(
map = mapOf(Relations.TYPE to ObjectTypeIds.OBJECT_TYPE)
)
)
)
@ -113,9 +94,9 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
verifyBlocking(createDataViewObject, times(1)) {
invoke(
CreateDataViewObject.Params(
template = null,
type = type
CreateDataViewObject.Params.SetByType(
type = givenType,
filters = viewer.filters
)
)
}
@ -133,24 +114,14 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
MockDataFactory.randomUuid()
)
val viewer = DVViewer(
id = MockDataFactory.randomUuid(),
filters = emptyList(),
sorts = emptyList(),
type = Block.Content.DataView.Viewer.Type.GRID,
name = MockDataFactory.randomString(),
viewerRelations = emptyList()
val viewer = StubDataViewView(
type = Block.Content.DataView.Viewer.Type.GRID
)
val dv = Block(
val dv = StubDataView(
id = MockDataFactory.randomUuid(),
content = DV(
sources = listOf(MockDataFactory.randomString()),
relations = emptyList(),
viewers = listOf(viewer)
),
children = emptyList(),
fields = Block.Fields.empty()
views = listOf(viewer),
sources = listOf(MockDataFactory.randomString())
)
val newObjectId = MockDataFactory.randomUuid()
@ -176,6 +147,9 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
map = mapOf(
Relations.SET_OF to listOf(givenType)
)
),
givenType to Block.Fields(
map = mapOf(Relations.TYPE to ObjectTypeIds.OBJECT_TYPE)
)
)
)
@ -191,9 +165,9 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
verifyBlocking(createDataViewObject, times(1)) {
invoke(
CreateDataViewObject.Params(
template = null,
type = givenType
CreateDataViewObject.Params.SetByType(
type = givenType,
filters = viewer.filters
)
)
}
@ -250,6 +224,9 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
map = mapOf(
Relations.SET_OF to listOf(givenType)
)
),
givenType to Block.Fields(
map = mapOf(Relations.TYPE to ObjectTypeIds.OBJECT_TYPE)
)
)
)
@ -265,9 +242,9 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
verifyBlocking(createDataViewObject, times(1)) {
invoke(
CreateDataViewObject.Params(
template = givenTemplate,
type = givenType
CreateDataViewObject.Params.SetByType(
type = givenType,
filters = viewer.filters
)
)
}
@ -309,7 +286,7 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
val givenTemplate = MockDataFactory.randomUuid()
val source = MockDataFactory.randomString()
val givenType = MockDataFactory.randomString()
val filter = DVFilter(
relationKey = relationStakeholderKey,
@ -340,7 +317,7 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
val dv = Block(
id = MockDataFactory.randomUuid(),
content = DV(
sources = listOf(source),
sources = listOf(givenType),
relations = listOf(relationStakeHolders),
viewers = listOf(viewer)
),
@ -356,7 +333,7 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
stubSearchWithSubscription()
stubSubscriptionEventChannel()
stubGetTemplates(
type = source,
type = givenType,
templates = listOf(givenTemplate)
)
stubOpenObjectSet(
@ -369,8 +346,11 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
mapOf(
root to Block.Fields(
map = mapOf(
Relations.SET_OF to listOf(source)
Relations.SET_OF to listOf(givenType)
)
),
givenType to Block.Fields(
map = mapOf(Relations.TYPE to ObjectTypeIds.OBJECT_TYPE)
)
)
)
@ -386,10 +366,9 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
verifyBlocking(createDataViewObject, times(1)) {
invoke(
CreateDataViewObject.Params(
template = givenTemplate,
prefilled = mapOf(relationStakeholderKey to relationStakeholderValue),
type = source
CreateDataViewObject.Params.SetByType(
type = givenType,
filters = viewer.filters
)
)
}

View file

@ -1,8 +1,9 @@
package com.anytypeio.anytype.presentation.sets.main
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
@ -17,25 +18,8 @@ import org.mockito.kotlin.verifyNoInteractions
class ObjectSetInitializationTest : ObjectSetViewModelTestSetup() {
private val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
style = Block.Content.Text.Style.TITLE,
text = MockDataFactory.randomString(),
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
private val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
private val title = StubTitle()
private val header = StubHeader(children = listOf(title.id))
private val ctx: Id = MockDataFactory.randomUuid()

View file

@ -12,6 +12,7 @@ import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.RelationLink
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SearchResult
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubRelationObject
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.core_models.ext.content
@ -61,15 +62,7 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
}
private val title = StubTitle()
private val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
private val header = StubHeader(children = listOf(title.id))
private val linkedProjectRelation = StubRelationObject(
key = MockDataFactory.randomString(),

View file

@ -168,7 +168,6 @@ open class ObjectSetViewModelTestSetup {
analytics = analytics,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
getTemplates = getTemplates,
createNewObject = createNewObject,
setDataViewSource = setDataViewSource,
setObjectDetails = setObjectDetails,

View file

@ -0,0 +1,163 @@
package com.anytypeio.anytype.presentation.sets.main
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.ObjectTypeIds
import com.anytypeio.anytype.core_models.RelationLink
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubDataView
import com.anytypeio.anytype.core_models.StubDataViewView
import com.anytypeio.anytype.core_models.StubDataViewViewRelation
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubRelationObject
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyBlocking
class SetByRelationTest : ObjectSetViewModelTestSetup() {
private val title = StubTitle()
private val header = StubHeader(children = listOf(title.id))
private val relations = listOf(
StubRelationObject(),
StubRelationObject(),
StubRelationObject()
)
private val viewer = StubDataViewView(
viewerRelations = relations.map {
StubDataViewViewRelation(
key = it.key
)
}
)
private val dv = StubDataView(
views = listOf(viewer),
relations = relations.map {
RelationLink(
key = it.key,
format = it.relationFormat
)
}
)
@get:Rule
val coroutineTestRule = CoroutinesTestRule()
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
initDataViewSubscriptionContainer()
}
@Test
fun `should create new object with source object type if given set is aggregated by specific object type`() {
// SETUP
val givenSourceType = MockDataFactory.randomUuid()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchWithSubscription()
stubSubscriptionEventChannel()
stubOpenObjectSet(
doc = listOf(
header,
title,
dv
),
details = Block.Details(
mapOf(
root to Block.Fields(
map = mapOf(Relations.SET_OF to listOf(givenSourceType))
),
givenSourceType to Block.Fields(
map = mapOf(Relations.TYPE to ObjectTypeIds.OBJECT_TYPE)
)
)
)
)
stubGetTemplates(type = givenSourceType)
val vm = givenViewModel()
// TESTING
with(vm) {
onStart(root)
onCreateNewDataViewObject()
}
verifyBlocking(createDataViewObject, times(1)) {
invoke(
CreateDataViewObject.Params.SetByType(
type = givenSourceType,
filters = viewer.filters
)
)
}
}
@Test
fun `should create new object with default object type if given set is aggregated by relations`() {
// SETUP
val givenSourceRelation = relations.random()
stubInterceptEvents()
stubInterceptThreadStatus()
stubSearchWithSubscription()
stubSubscriptionEventChannel()
stubOpenObjectSet(
doc = listOf(
header,
title,
dv
),
details = Block.Details(
mapOf(
root to Block.Fields(
map = mapOf(
Relations.SET_OF to listOf(givenSourceRelation.id)
)
),
givenSourceRelation.id to Block.Fields(
map = mapOf(
Relations.ID to givenSourceRelation.id,
Relations.RELATION_KEY to givenSourceRelation.key,
Relations.TYPE to ObjectTypeIds.RELATION
)
)
)
)
)
val vm = givenViewModel()
// TESTING
with(vm) {
onStart(root)
onCreateNewDataViewObject()
}
verifyBlocking(createDataViewObject, times(1)) {
invoke(
CreateDataViewObject.Params.SetByRelation(
filters = viewer.filters
)
)
}
}
}

View file

@ -0,0 +1,44 @@
package com.anytypeio.anytype.core_models
import com.anytypeio.anytype.test_utils.MockDataFactory
fun StubDataView(
id : Id = MockDataFactory.randomUuid(),
views: List<DVViewer> = emptyList(),
relations: List<RelationLink> = emptyList(),
sources: List<Id> = emptyList()
) : Block = Block(
id = id,
content = DV(
sources = sources,
relations = emptyList(),
relationsIndex= relations,
viewers = views
),
children = emptyList(),
fields = Block.Fields.empty()
)
fun StubDataViewView(
id : Id = MockDataFactory.randomUuid(),
filters: List<DVFilter> = emptyList(),
sorts: List<DVSort> = emptyList(),
type: DVViewerType = DVViewerType.GRID,
name: String = MockDataFactory.randomString(),
viewerRelations: List<DVViewerRelation> = emptyList()
) : DVViewer = DVViewer(
id = id,
filters = filters,
sorts = sorts,
type = type,
name = name,
viewerRelations = viewerRelations
)
fun StubDataViewViewRelation(
key: Key = MockDataFactory.randomUuid(),
isVisible: Boolean = MockDataFactory.randomBoolean()
) : DVViewerRelation = DVViewerRelation(
key = key,
isVisible = isVisible
)