From 326599d06525cb1d976ca8e713117f87677248fe Mon Sep 17 00:00:00 2001 From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:38:30 +0100 Subject: [PATCH] DROID-1030 Collection | Enhancement | Order of objects (#3036) * DROID-1030 objectOrders model, added to data view content * DROID-1030 grid order * DROID-1030 gallery order * DROID-1030 list order * DROID-1030 get object order by view id * DROID-1030 refactoring * DROID-1030 update old tests * DROID-1030 object order tests by view --- .../anytypeio/anytype/core_models/Block.kt | 3 +- .../anytype/core_models/ObjectOrder.kt | 7 + .../anytype/middleware/mappers/Alias.kt | 1 + .../middleware/mappers/ToCoreModelMappers.kt | 11 +- .../relations/ObjectSetRenderMapper.kt | 25 +- .../presentation/sets/GalleryViewMapper.kt | 6 +- .../presentation/sets/ListViewMapper.kt | 161 +++++---- .../presentation/sets/ObjectSetExtension.kt | 4 + .../presentation/sets/ObjectSetViewModel.kt | 12 +- .../collections/CollectionAddRelationTest.kt | 22 +- .../CollectionDataViewUpdateTest.kt | 2 +- .../collections/MockCollection.kt | 134 ++++++- .../ObjectStateCollectionViewTest.kt | 335 ++++++++++++++++++ .../sets/main/ObjectSetViewModelTestSetup.kt | 5 +- .../anytypeio/anytype/core_models/DataView.kt | 6 +- 15 files changed, 626 insertions(+), 108 deletions(-) create mode 100644 core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectOrder.kt diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt index 1b92704b12..b2cb5562d4 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt @@ -288,7 +288,8 @@ data class Block( val viewers: List, val relationLinks: List = emptyList(), val targetObjectId: Id = "", - val isCollection: Boolean = false + val isCollection: Boolean = false, + val objectOrders: List = emptyList() ) : Content() { data class Viewer( diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectOrder.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectOrder.kt new file mode 100644 index 0000000000..46af71782f --- /dev/null +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectOrder.kt @@ -0,0 +1,7 @@ +package com.anytypeio.anytype.core_models + +data class ObjectOrder( + val view: Id, + val group: Id, + val ids: List +) diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt index 402f4e5284..ca5aab2641 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt @@ -47,6 +47,7 @@ typealias MDVFilterUpdate = anytype.Event.Block.Dataview.ViewUpdate.Filter typealias MDVSortUpdate = anytype.Event.Block.Dataview.ViewUpdate.Sort typealias MDVRelationUpdate = anytype.Event.Block.Dataview.ViewUpdate.Relation typealias MDVViewFields = anytype.Event.Block.Dataview.ViewUpdate.Fields +typealias MDVObjectOrder = anytype.model.Block.Content.Dataview.ObjectOrder typealias MObjectType = anytype.model.ObjectType typealias MSmartBlockType = anytype.model.SmartBlockType diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt index c4807cb027..d7fdc432d0 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt @@ -354,7 +354,16 @@ fun MBlock.toCoreModelsDataView(): Block.Content.DataView { viewers = content.views.map { it.toCoreModels() }, relationLinks = content.relationLinks.map { it.toCoreModels() }, targetObjectId = content.TargetObjectId, - isCollection = content.isCollection + isCollection = content.isCollection, + objectOrders = content.objectOrders.map { it.toCoreModelsObjectOrder() } + ) +} + +fun MDVObjectOrder.toCoreModelsObjectOrder(): ObjectOrder { + return ObjectOrder( + view = viewId, + group = groupId, + ids = objectIds ) } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt index ffa91da7a7..c6a3c0dcd6 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt @@ -76,7 +76,8 @@ suspend fun DVViewer.render( objects: List, details: Map, dataViewRelations: List, - store: ObjectStore + store: ObjectStore, + objectOrderIds: List = emptyList() ): Viewer { return when (type) { DVViewerType.GRID -> { @@ -85,7 +86,8 @@ suspend fun DVViewer.render( objects = objects, details = details, builder = builder, - store = store + store = store, + objectOrderIds = objectOrderIds ) } DVViewerType.GALLERY -> { @@ -97,7 +99,8 @@ suspend fun DVViewer.render( relations = dataViewRelations, coverImageHashProvider = coverImageHashProvider, urlBuilder = builder, - objectStore = store + objectStore = store, + objectOrderIds = objectOrderIds ), title = name, largeCards = cardSize != DVViewerCardSize.SMALL @@ -116,7 +119,8 @@ suspend fun DVViewer.render( details = details, relations = visibleRelations, urlBuilder = builder, - store = store + store = store, + objectOrderIds = objectOrderIds ), title = name ) @@ -128,7 +132,8 @@ suspend fun DVViewer.render( objects = objects, details = details, builder = builder, - store = store + store = store, + objectOrderIds = objectOrderIds ) } else { Viewer.Unsupported( @@ -141,12 +146,18 @@ suspend fun DVViewer.render( } } +private fun List.sortObjects(objectOrderIds: List): List { + val orderMap = objectOrderIds.mapIndexed { index, id -> id to index }.toMap() + return sortedBy { orderMap[it.id] } +} + private suspend fun DVViewer.buildGridView( dataViewRelations: List, objects: List, details: Map, builder: UrlBuilder, - store: ObjectStore + store: ObjectStore, + objectOrderIds: List ): Viewer { val vmap = viewerRelations.associateBy { it.key } val visibleRelations = dataViewRelations.filter { relation -> @@ -173,7 +184,7 @@ private suspend fun DVViewer.buildGridView( id = id, name = name, columns = columns, - rows = rows + rows = if (objectOrderIds.isNotEmpty()) rows.sortObjects(objectOrderIds) else rows ) } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/GalleryViewMapper.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/GalleryViewMapper.kt index 615aa4fb6a..3961cda721 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/GalleryViewMapper.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/GalleryViewMapper.kt @@ -30,7 +30,8 @@ suspend fun DVViewer.buildGalleryViews( details: Map, coverImageHashProvider: CoverImageHashProvider, urlBuilder: UrlBuilder, - objectStore: ObjectStore + objectStore: ObjectStore, + objectOrderIds: List ): List { val filteredRelations = viewerRelations.mapNotNull { setting -> @@ -43,6 +44,8 @@ suspend fun DVViewer.buildGalleryViews( val hasCover = !coverRelationKey.isNullOrEmpty() + val orderMap = objectOrderIds.mapIndexed { index, id -> id to index }.toMap() + return objectIds .mapNotNull { objectStore.get(it) } .map { obj -> @@ -67,6 +70,7 @@ suspend fun DVViewer.buildGalleryViews( ) } } + .sortedBy { item -> orderMap[item.objectId] } } private suspend fun ObjectWrapper.Basic.mapToDefaultItem( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ListViewMapper.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ListViewMapper.kt index 514c4ee2ff..6101f43d37 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ListViewMapper.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ListViewMapper.kt @@ -18,81 +18,94 @@ suspend fun DVViewer.buildListViews( details: Map, urlBuilder: UrlBuilder, store: ObjectStore, -): List = objects.mapNotNull { id -> - val obj = store.get(id) - if (obj != null) { - when (obj.layout) { - ObjectType.Layout.BASIC, - ObjectType.Layout.SET, - ObjectType.Layout.COLLECTION, - ObjectType.Layout.OBJECT_TYPE, - ObjectType.Layout.RELATION, - ObjectType.Layout.FILE, - ObjectType.Layout.IMAGE, - ObjectType.Layout.NOTE, - ObjectType.Layout.BOOKMARK -> { - Viewer.ListView.Item.Default( - objectId = obj.id, - relations = obj.valuesFilteredByHidden( - relations = relations, - urlBuilder = urlBuilder, - details = details, - settings = viewerRelations, - storeOfObjects = store - ), - name = obj.getProperName(), - icon = ObjectIcon.from( - obj = obj, - layout = obj.layout, - builder = urlBuilder - ), - description = obj.description, - hideIcon = hideIcon - ) + objectOrderIds: List +): List { + val items = objects.mapNotNull { id -> + val obj = store.get(id) + if (obj != null) { + when (obj.layout) { + ObjectType.Layout.BASIC, + ObjectType.Layout.SET, + ObjectType.Layout.COLLECTION, + ObjectType.Layout.OBJECT_TYPE, + ObjectType.Layout.RELATION, + ObjectType.Layout.FILE, + ObjectType.Layout.IMAGE, + ObjectType.Layout.NOTE, + ObjectType.Layout.BOOKMARK -> { + Viewer.ListView.Item.Default( + objectId = obj.id, + relations = obj.valuesFilteredByHidden( + relations = relations, + urlBuilder = urlBuilder, + details = details, + settings = viewerRelations, + storeOfObjects = store + ), + name = obj.getProperName(), + icon = ObjectIcon.from( + obj = obj, + layout = obj.layout, + builder = urlBuilder + ), + description = obj.description, + hideIcon = hideIcon + ) + } + ObjectType.Layout.PROFILE -> { + Viewer.ListView.Item.Profile( + objectId = obj.id, + relations = obj.valuesFilteredByHidden( + relations = relations, + urlBuilder = urlBuilder, + details = details, + settings = viewerRelations, + storeOfObjects = store + ), + name = obj.getProperName(), + icon = ObjectIcon.from( + obj = obj, + layout = obj.layout, + builder = urlBuilder + ), + description = obj.description, + hideIcon = hideIcon + ) + } + ObjectType.Layout.TODO -> { + Viewer.ListView.Item.Task( + objectId = obj.id, + relations = obj.valuesFilteredByHidden( + relations = relations, + urlBuilder = urlBuilder, + details = details, + settings = viewerRelations, + storeOfObjects = store + ), + name = obj.getProperName(), + icon = ObjectIcon.from( + obj = obj, + layout = obj.layout, + builder = urlBuilder + ), + description = obj.description, + hideIcon = hideIcon + ) + } + else -> null } - ObjectType.Layout.PROFILE -> { - Viewer.ListView.Item.Profile( - objectId = obj.id, - relations = obj.valuesFilteredByHidden( - relations = relations, - urlBuilder = urlBuilder, - details = details, - settings = viewerRelations, - storeOfObjects = store - ), - name = obj.getProperName(), - icon = ObjectIcon.from( - obj = obj, - layout = obj.layout, - builder = urlBuilder - ), - description = obj.description, - hideIcon = hideIcon - ) - } - ObjectType.Layout.TODO -> { - Viewer.ListView.Item.Task( - objectId = obj.id, - relations = obj.valuesFilteredByHidden( - relations = relations, - urlBuilder = urlBuilder, - details = details, - settings = viewerRelations, - storeOfObjects = store - ), - name = obj.getProperName(), - icon = ObjectIcon.from( - obj = obj, - layout = obj.layout, - builder = urlBuilder - ), - description = obj.description, - hideIcon = hideIcon - ) - } - else -> null + } else { + null } - } else { - null } + return if (objectOrderIds.isNotEmpty()) { + items.sortObjects(objectOrderIds) + } else { + items + } +} + +private fun List.sortObjects(objectOrderIds: List): List { + val orderMap = objectOrderIds.mapIndexed { index, id -> id to index }.toMap() + return sortedBy { orderMap[it.objectId] } } \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt index 4be684c508..795432a2f1 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt @@ -194,6 +194,10 @@ fun ObjectState.DataView.viewerById(currentViewerId: String?): DVViewer? { ?: dataViewContent.viewers.firstOrNull() } +fun ObjectState.DataView.Collection.getObjectOrderIds(currentViewerId: String): List { + return dataViewContent.objectOrders.find { it.view == currentViewerId }?.ids ?: emptyList() +} + suspend fun List.updateFormatForSubscription(storeOfRelations: StoreOfRelations): List { return map { f: DVFilter -> val r = storeOfRelations.getByKey(f.relation) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt index 7e22342730..cc36759e1f 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt @@ -403,15 +403,21 @@ class ObjectSetViewModel( state.dataViewContent.relationLinks.mapNotNull { storeOfRelations.getByKey(it.key) } - val viewer = state.viewerById(currentViewId) - ?.render( + val dvViewer = state.viewerById(currentViewId) + val viewer = if (dvViewer != null) { + val objectOrderIds = state.getObjectOrderIds(dvViewer.id) + dvViewer.render( coverImageHashProvider = coverImageHashProvider, builder = urlBuilder, objects = db.objects, dataViewRelations = relations, details = state.details, - store = objectStore + store = objectStore, + objectOrderIds = objectOrderIds ) + } else { + null + } when { viewer == null -> DataViewViewState.Collection.NoView viewer.isEmpty() -> DataViewViewState.Collection.NoItems(title = viewer.title) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionAddRelationTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionAddRelationTest.kt index 76e45053d4..b66901c73f 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionAddRelationTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionAddRelationTest.kt @@ -115,7 +115,7 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() { val noItemsState = awaitItem() assertEquals( - expected = DataViewViewState.Collection.NoItems(title = objectCollection.viewer.name), + expected = DataViewViewState.Collection.NoItems(title = objectCollection.viewerList.name), actual = noItemsState ) @@ -123,8 +123,8 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() { assertEquals( expected = DataViewViewState.Collection.Default( viewer = Viewer.ListView( - id = objectCollection.viewer.id, - title = objectCollection.viewer.name, + id = objectCollection.viewerList.id, + title = objectCollection.viewerList.name, items = listOf( Viewer.ListView.Item.Default( objectId = objectCollection.obj1.id, @@ -220,7 +220,7 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() { val eventDataViewUpdateView = Event.Command.DataView.UpdateView( context = root, block = objectCollection.dataView.id, - viewerId = objectCollection.viewer.id, + viewerId = objectCollection.viewerList.id, relationUpdates = listOf( Event.Command.DataView.UpdateView.DVViewerRelationUpdate.Add( afterId = objectCollection.relationObject2.id, @@ -231,6 +231,15 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() { filterUpdates = listOf(), sortUpdates = listOf() ) + stubSubscriptionResults( + subscription = objectCollection.subscriptionId, + collection = root, + workspace = objectCollection.workspaceId, + storeOfRelations = storeOfRelations, + keys = objectCollection.dvKeys + relationObject4.key, + objects = listOf(objectCollection.obj1, objectCollection.obj2), + dvSorts = objectCollection.sorts + ) dispatcher.send( Payload( context = root, @@ -244,7 +253,7 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() { val second = awaitItem() - val expectedView = objectCollection.viewer.copy( + val expectedView = objectCollection.viewerList.copy( viewerRelations = listOf( objectCollection.dvViewerRelation1, objectCollection.dvViewerRelation2, @@ -261,6 +270,9 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() { objectCollection.relationLink1, objectCollection.relationLink2, objectCollection.relationLink3, + objectCollection.relationLink4, + objectCollection.relationLink5, + objectCollection.relationLink6, relationLink4 ) ) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionDataViewUpdateTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionDataViewUpdateTest.kt index 8e8436f75b..a5de3d3d4d 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionDataViewUpdateTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/CollectionDataViewUpdateTest.kt @@ -78,7 +78,7 @@ class CollectionDataViewUpdateTest : ObjectSetViewModelTestSetup() { val eventDVUpdate = Event.Command.DataView.UpdateView( context = objectCollection.root, block = objectCollection.dataView.id, - viewerId = objectCollection.viewer.id, + viewerId = objectCollection.viewerList.id, sortUpdates = listOf( Event.Command.DataView.UpdateView.DVSortUpdate.Update( id = objectCollection.sort1.id, diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockCollection.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockCollection.kt index 7d883f49ed..4cfad5527e 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockCollection.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/MockCollection.kt @@ -4,6 +4,7 @@ import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.DVSort import com.anytypeio.anytype.core_models.DVSortType import com.anytypeio.anytype.core_models.DVViewerType +import com.anytypeio.anytype.core_models.ObjectOrder import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.Relation import com.anytypeio.anytype.core_models.Relations @@ -16,7 +17,6 @@ import com.anytypeio.anytype.core_models.StubRelationLink import com.anytypeio.anytype.core_models.StubRelationObject import com.anytypeio.anytype.core_models.StubTitle import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription -import net.bytebuddy.asm.Advice.OffsetMapping.Sort import net.bytebuddy.utility.RandomString class MockCollection(context: String) { @@ -40,6 +40,21 @@ class MockCollection(context: String) { isReadOnlyValue = false, format = Relation.Format.TAG ) + val relationObject4 = StubRelationObject( + key = "relationStatus-${RandomString.make()}", + isReadOnlyValue = false, + format = Relation.Format.STATUS + ) + val relationObject5 = StubRelationObject( + key = "relationObject-${RandomString.make()}", + isReadOnlyValue = false, + format = Relation.Format.OBJECT + ) + val relationObject6 = StubRelationObject( + key = "relationNumber-${RandomString.make()}", + isReadOnlyValue = false, + format = Relation.Format.NUMBER + ) // VIEW RELATIONS val dvViewerRelation1 = @@ -48,17 +63,29 @@ class MockCollection(context: String) { StubDataViewViewRelation(key = relationObject2.key, isVisible = true) val dvViewerRelation3 = StubDataViewViewRelation(key = relationObject3.key, isVisible = true) + val dvViewerRelation4 = + StubDataViewViewRelation(key = relationObject4.key, isVisible = true) + val dvViewerRelation5 = + StubDataViewViewRelation(key = relationObject5.key, isVisible = true) + val dvViewerRelation6 = + StubDataViewViewRelation(key = relationObject6.key, isVisible = true) // RELATION LINKS val relationLink1 = StubRelationLink(relationObject1.key) val relationLink2 = StubRelationLink(relationObject2.key) val relationLink3 = StubRelationLink(relationObject3.key) + val relationLink4 = StubRelationLink(relationObject4.key) + val relationLink5 = StubRelationLink(relationObject5.key) + val relationLink6 = StubRelationLink(relationObject6.key) // SEARCH OBJECTS COMMAND, RELATION KEYS val dvKeys = listOf( relationObject1.key, relationObject2.key, - relationObject3.key + relationObject3.key, + relationObject4.key, + relationObject5.key, + relationObject6.key ) // SORTS @@ -67,23 +94,106 @@ class MockCollection(context: String) { relationKey = relationObject1.key, type = DVSortType.ASC ) + val sortGrid = DVSort( + id = "sortId-${RandomString.make()}", + relationKey = relationObject5.key, + type = DVSortType.DESC + ) + val sortGallery = DVSort( + id = "sortId-${RandomString.make()}", + relationKey = relationObject6.key, + type = DVSortType.DESC + ) val sorts = listOf(sort1) - val viewer = - StubDataViewView( - id = "dvViewer-${RandomString.make()}", - viewerRelations = listOf(dvViewerRelation1, dvViewerRelation2, dvViewerRelation3), - type = DVViewerType.LIST, - sorts = sorts + val viewerList = StubDataViewView( + id = "dvViewerList-${RandomString.make()}", + viewerRelations = listOf(dvViewerRelation1, dvViewerRelation2, dvViewerRelation3), + type = DVViewerType.LIST, + sorts = sorts + ) + val viewerGrid = StubDataViewView( + id = "dvViewerGrid-${RandomString.make()}", + viewerRelations = listOf(dvViewerRelation6, dvViewerRelation5, dvViewerRelation3), + type = DVViewerType.GRID, + sorts = listOf(sortGrid) + ) + val viewerGallery = StubDataViewView( + id = "dvViewerGallery-${RandomString.make()}", + viewerRelations = listOf( + dvViewerRelation1, + dvViewerRelation2, + dvViewerRelation3, + dvViewerRelation4, + dvViewerRelation5, + dvViewerRelation6 + ), + type = DVViewerType.GALLERY, + sorts = listOf(sortGallery) + ) + + val obj1 = StubObject(id = "object1-${RandomString.make()}") + val obj2 = StubObject(id = "object2-${RandomString.make()}") + val obj3 = StubObject(id = "object3-${RandomString.make()}") + val obj4 = StubObject(id = "object4-${RandomString.make()}") + val obj5 = StubObject(id = "object5-${RandomString.make()}") + + // 3, 2, 4, 5, 1 + val objectOrderList = + ObjectOrder( + view = viewerList.id, + group = "", + ids = listOf(obj3.id, obj2.id, obj4.id, obj5.id, obj1.id) + ) + + // 1, 2, 4, 3, 5 + val objectOrderGrid = + ObjectOrder( + view = viewerGrid.id, + group = "", + ids = listOf(obj1.id, obj2.id, obj4.id, obj3.id, obj5.id) + ) + + // 5, 4, 3, 2, 1 + val objectOrderGallery = + ObjectOrder( + view = viewerGallery.id, + group = "", + ids = listOf(obj5.id, obj4.id, obj3.id, obj2.id, obj1.id) ) val dataView = StubDataView( id = "dv-${RandomString.make()}", - views = listOf(viewer), - relationLinks = listOf(relationLink1, relationLink2, relationLink3) + views = listOf(viewerList), + relationLinks = listOf(relationLink1, relationLink2, relationLink3, relationLink4, relationLink5, relationLink6), + isCollection = true, + objectOrder = emptyList() + ) + + val dataViewGrid = StubDataView( + id = "dv-${RandomString.make()}", + views = listOf(viewerGrid), + relationLinks = listOf(relationLink1, relationLink2, relationLink3, relationLink4, relationLink5, relationLink6), + isCollection = true, + objectOrder = emptyList() + ) + + val dataViewGallery = StubDataView( + id = "dv-${RandomString.make()}", + views = listOf(viewerGallery), + relationLinks = listOf(relationLink1, relationLink2, relationLink3, relationLink4, relationLink5, relationLink6), + isCollection = true, + objectOrder = emptyList() + ) + + val dataViewWithObjectOrder = StubDataView( + id = "dv-${RandomString.make()}", + views = listOf(viewerList, viewerGrid, viewerGallery), + relationLinks = listOf(relationLink1, relationLink2, relationLink3, relationLink4, relationLink5, relationLink6), + objectOrder = listOf(objectOrderList, objectOrderGrid, objectOrderGallery), + isCollection = true ) val workspaceId = "workspace-${RandomString.make()}" - val obj1 = StubObject(id = "object1-${RandomString.make()}") - val obj2 = StubObject(id = "object2-${RandomString.make()}") + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(context) val details = Block.Details( diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt index 524f004593..8c0f98b668 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectStateCollectionViewTest.kt @@ -4,6 +4,7 @@ import app.cash.turbine.testIn import com.anytypeio.anytype.presentation.sets.DataViewViewState import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup +import com.anytypeio.anytype.presentation.sets.model.Viewer import com.anytypeio.anytype.presentation.sets.state.ObjectState import kotlin.test.assertEquals import kotlin.test.assertIs @@ -179,4 +180,338 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() { assertIs(viewerFlow.awaitItem()) assertIs(viewerFlow.awaitItem()) } + + @Test + fun `should sort LIST collection objects by data view object order`() = runTest { + // SETUP + stubWorkspaceManager(mockObjectCollection.workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf( + mockObjectCollection.header, + mockObjectCollection.title, + mockObjectCollection.dataViewWithObjectOrder + ), + details = mockObjectCollection.details + ) + stubStoreOfRelations(mockObjectCollection) + stubSubscriptionResults( + subscription = mockObjectCollection.subscriptionId, + workspace = mockObjectCollection.workspaceId, + collection = root, + storeOfRelations = storeOfRelations, + keys = mockObjectCollection.dvKeys, + objects = listOf( + mockObjectCollection.obj1, + mockObjectCollection.obj2, + mockObjectCollection.obj3, + mockObjectCollection.obj4, + mockObjectCollection.obj5 + ), + dvSorts = mockObjectCollection.sorts + ) + + // TESTING + viewModel.onStart(ctx = root) + + val viewerFlow = viewModel.currentViewer.testIn(backgroundScope) + val stateFlow = stateReducer.state.testIn(backgroundScope) + + // ASSERT STATES + assertIs(stateFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()) + + assertIs(viewerFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()).also {dataViewState -> + val rows = (dataViewState.viewer as Viewer.ListView).items + assertEquals(5, rows.size) + + // SHOULD BE 3, 2, 4, 5, 1 + assertEquals(mockObjectCollection.obj3.id, rows[0].objectId) + assertEquals(mockObjectCollection.obj2.id, rows[1].objectId) + assertEquals(mockObjectCollection.obj4.id, rows[2].objectId) + assertEquals(mockObjectCollection.obj5.id, rows[3].objectId) + assertEquals(mockObjectCollection.obj1.id, rows[4].objectId) + } + } + + @Test + fun `should not sort LIST collection objects by empty data view object order`() = runTest { + // SETUP + stubWorkspaceManager(mockObjectCollection.workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf( + mockObjectCollection.header, + mockObjectCollection.title, + mockObjectCollection.dataView + ), + details = mockObjectCollection.details + ) + stubStoreOfRelations(mockObjectCollection) + stubSubscriptionResults( + subscription = mockObjectCollection.subscriptionId, + workspace = mockObjectCollection.workspaceId, + collection = root, + storeOfRelations = storeOfRelations, + keys = mockObjectCollection.dvKeys, + objects = listOf( + mockObjectCollection.obj5, + mockObjectCollection.obj2, + mockObjectCollection.obj3, + mockObjectCollection.obj4, + mockObjectCollection.obj1 + ), + dvSorts = mockObjectCollection.sorts + ) + + // TESTING + viewModel.onStart(ctx = root) + + val viewerFlow = viewModel.currentViewer.testIn(backgroundScope) + val stateFlow = stateReducer.state.testIn(backgroundScope) + + // ASSERT STATES + assertIs(stateFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()) + + assertIs(viewerFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()).also {dataViewState -> + val rows = (dataViewState.viewer as Viewer.ListView).items + assertEquals(5, rows.size) + + // SHOULD BE NOT SORTED 5, 2, 3, 4, 1 + assertEquals(mockObjectCollection.obj5.id, rows[0].objectId) + assertEquals(mockObjectCollection.obj2.id, rows[1].objectId) + assertEquals(mockObjectCollection.obj3.id, rows[2].objectId) + assertEquals(mockObjectCollection.obj4.id, rows[3].objectId) + assertEquals(mockObjectCollection.obj1.id, rows[4].objectId) + } + } + + @Test + fun `should sort GRID collection objects by data view object order`() = runTest { + // SETUP + session.currentViewerId.value = mockObjectCollection.viewerGrid.id + stubWorkspaceManager(mockObjectCollection.workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf( + mockObjectCollection.header, + mockObjectCollection.title, + mockObjectCollection.dataViewWithObjectOrder + ), + details = mockObjectCollection.details + ) + stubStoreOfRelations(mockObjectCollection) + stubSubscriptionResults( + subscription = mockObjectCollection.subscriptionId, + workspace = mockObjectCollection.workspaceId, + collection = root, + storeOfRelations = storeOfRelations, + keys = mockObjectCollection.dvKeys, + objects = listOf( + mockObjectCollection.obj1, + mockObjectCollection.obj2, + mockObjectCollection.obj3, + mockObjectCollection.obj4, + mockObjectCollection.obj5 + ), + dvSorts = listOf(mockObjectCollection.sortGrid) + ) + + // TESTING + viewModel.onStart(ctx = root) + + val viewerFlow = viewModel.currentViewer.testIn(backgroundScope) + val stateFlow = stateReducer.state.testIn(backgroundScope) + + // ASSERT STATES + assertIs(stateFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()) + + assertIs(viewerFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()).also {dataViewState -> + val rows = (dataViewState.viewer as Viewer.GridView).rows + assertEquals(5, rows.size) + + // SHOULD BE SORTED 1, 2, 4, 3, 5 + assertEquals(mockObjectCollection.obj1.id, rows[0].id) + assertEquals(mockObjectCollection.obj2.id, rows[1].id) + assertEquals(mockObjectCollection.obj4.id, rows[2].id) + assertEquals(mockObjectCollection.obj3.id, rows[3].id) + assertEquals(mockObjectCollection.obj5.id, rows[4].id) + } + } + + @Test + fun `should NOT sort GRID collection objects by empty data view object order`() = runTest { + // SETUP + session.currentViewerId.value = mockObjectCollection.viewerGrid.id + stubWorkspaceManager(mockObjectCollection.workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf( + mockObjectCollection.header, + mockObjectCollection.title, + mockObjectCollection.dataViewGrid + ), + details = mockObjectCollection.details + ) + stubStoreOfRelations(mockObjectCollection) + stubSubscriptionResults( + subscription = mockObjectCollection.subscriptionId, + workspace = mockObjectCollection.workspaceId, + collection = root, + storeOfRelations = storeOfRelations, + keys = mockObjectCollection.dvKeys, + objects = listOf( + mockObjectCollection.obj1, + mockObjectCollection.obj2, + mockObjectCollection.obj3, + mockObjectCollection.obj4, + mockObjectCollection.obj5 + ), + dvSorts = listOf(mockObjectCollection.sortGrid) + ) + + // TESTING + viewModel.onStart(ctx = root) + + val viewerFlow = viewModel.currentViewer.testIn(backgroundScope) + val stateFlow = stateReducer.state.testIn(backgroundScope) + + // ASSERT STATES + assertIs(stateFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()) + + assertIs(viewerFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()).also {dataViewState -> + val rows = (dataViewState.viewer as Viewer.GridView).rows + assertEquals(5, rows.size) + + // SHOULD BE SORTED 1, 2, 4, 3, 5 + assertEquals(mockObjectCollection.obj1.id, rows[0].id) + assertEquals(mockObjectCollection.obj2.id, rows[1].id) + assertEquals(mockObjectCollection.obj3.id, rows[2].id) + assertEquals(mockObjectCollection.obj4.id, rows[3].id) + assertEquals(mockObjectCollection.obj5.id, rows[4].id) + } + } + + @Test + fun `should sort GALLERY collection objects by data view object order`() = runTest { + // SETUP + session.currentViewerId.value = mockObjectCollection.viewerGallery.id + stubWorkspaceManager(mockObjectCollection.workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf( + mockObjectCollection.header, + mockObjectCollection.title, + mockObjectCollection.dataViewWithObjectOrder + ), + details = mockObjectCollection.details + ) + stubStoreOfRelations(mockObjectCollection) + stubSubscriptionResults( + subscription = mockObjectCollection.subscriptionId, + workspace = mockObjectCollection.workspaceId, + collection = root, + storeOfRelations = storeOfRelations, + keys = mockObjectCollection.dvKeys, + objects = listOf( + mockObjectCollection.obj1, + mockObjectCollection.obj2, + mockObjectCollection.obj3, + mockObjectCollection.obj4, + mockObjectCollection.obj5 + ), + dvSorts = listOf(mockObjectCollection.sortGallery) + ) + + // TESTING + viewModel.onStart(ctx = root) + + val viewerFlow = viewModel.currentViewer.testIn(backgroundScope) + val stateFlow = stateReducer.state.testIn(backgroundScope) + + // ASSERT STATES + assertIs(stateFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()) + + assertIs(viewerFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()).also {dataViewState -> + val rows = (dataViewState.viewer as Viewer.GalleryView).items + assertEquals(5, rows.size) + + // SHOULD BE SORTED 5, 4, 3, 2, 1 + assertEquals(mockObjectCollection.obj5.id, rows[0].objectId) + assertEquals(mockObjectCollection.obj4.id, rows[1].objectId) + assertEquals(mockObjectCollection.obj3.id, rows[2].objectId) + assertEquals(mockObjectCollection.obj2.id, rows[3].objectId) + assertEquals(mockObjectCollection.obj1.id, rows[4].objectId) + } + } + + @Test + fun `should NOT sort GALLERY collection objects by empty data view object order`() = runTest { + // SETUP + session.currentViewerId.value = mockObjectCollection.viewerGallery.id + stubWorkspaceManager(mockObjectCollection.workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf( + mockObjectCollection.header, + mockObjectCollection.title, + mockObjectCollection.dataViewGallery + ), + details = mockObjectCollection.details + ) + stubStoreOfRelations(mockObjectCollection) + stubSubscriptionResults( + subscription = mockObjectCollection.subscriptionId, + workspace = mockObjectCollection.workspaceId, + collection = root, + storeOfRelations = storeOfRelations, + keys = mockObjectCollection.dvKeys, + objects = listOf( + mockObjectCollection.obj1, + mockObjectCollection.obj2, + mockObjectCollection.obj3, + mockObjectCollection.obj4, + mockObjectCollection.obj5 + ), + dvSorts = listOf(mockObjectCollection.sortGallery) + ) + + // TESTING + viewModel.onStart(ctx = root) + + val viewerFlow = viewModel.currentViewer.testIn(backgroundScope) + val stateFlow = stateReducer.state.testIn(backgroundScope) + + // ASSERT STATES + assertIs(stateFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()) + + assertIs(viewerFlow.awaitItem()) + assertIs(viewerFlow.awaitItem()).also {dataViewState -> + val rows = (dataViewState.viewer as Viewer.GalleryView).items + assertEquals(5, rows.size) + + // SHOULD BE SORTED 5, 4, 3, 2, 1 + assertEquals(mockObjectCollection.obj1.id, rows[0].objectId) + assertEquals(mockObjectCollection.obj2.id, rows[1].objectId) + assertEquals(mockObjectCollection.obj3.id, rows[2].objectId) + assertEquals(mockObjectCollection.obj4.id, rows[3].objectId) + assertEquals(mockObjectCollection.obj5.id, rows[4].objectId) + } + } } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt index a48725433b..0f2ba95cd0 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt @@ -329,7 +329,10 @@ open class ObjectSetViewModelTestSetup { listOf( mockObjectCollection.relationObject1, mockObjectCollection.relationObject2, - mockObjectCollection.relationObject3 + mockObjectCollection.relationObject3, + mockObjectCollection.relationObject4, + mockObjectCollection.relationObject5, + mockObjectCollection.relationObject6 ) ) } diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt index 3af51b3193..789054823e 100644 --- a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt @@ -7,14 +7,16 @@ fun StubDataView( views: List = emptyList(), relationLinks: List = emptyList(), targetObjectId: Id = MockDataFactory.randomUuid(), - isCollection: Boolean = false + isCollection: Boolean = false, + objectOrder: List = emptyList() ): Block = Block( id = id, content = DV( relationLinks = relationLinks, viewers = views, targetObjectId = targetObjectId, - isCollection = isCollection + isCollection = isCollection, + objectOrders = objectOrder ), children = emptyList(), fields = Block.Fields.empty()