diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/Event.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/Event.kt index 25c86b5e30..099dc90aef 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/Event.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Event.kt @@ -260,6 +260,41 @@ sealed class Event { val dv: Id, val sources: List ) : DataView() + + data class UpdateView( + override val context: Id, + val block: Id, + val viewerId: Id, + val filterUpdates: List, + val sortUpdates: List, + val relationUpdates: List + ) : DataView() { + + sealed class DVFilterUpdate { + data class Add(val afterId: Id, val filters: List) : + DVFilterUpdate() + + data class Move(val afterId: Id, val ids: List) : DVFilterUpdate() + data class Remove(val ids: List) : DVFilterUpdate() + data class Update(val id: Id, val filter: DVFilter) : DVFilterUpdate() + } + + sealed class DVSortUpdate { + data class Add(val afterId: Id, val sorts: List) : DVSortUpdate() + data class Move(val afterId: Id, val ids: List) : DVSortUpdate() + data class Remove(val ids: List) : DVSortUpdate() + data class Update(val id: Id, val sort: DVSort) : DVSortUpdate() + } + + sealed class DVViewerRelationUpdate { + data class Add(val afterId: Id, val relations: List) : + DVViewerRelationUpdate() + data class Move(val afterId: Id, val ids: List) : DVViewerRelationUpdate() + data class Remove(val ids: List) : DVViewerRelationUpdate() + data class Update(val id: Id, val relation: DVViewerRelation) : + DVViewerRelationUpdate() + } + } } } } \ No newline at end of file diff --git a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/Extensions.kt b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/Extensions.kt index 9206b90ee6..bb5152a722 100644 --- a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/Extensions.kt +++ b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/Extensions.kt @@ -77,4 +77,37 @@ inline fun Fragment.withParent(action: T.() -> Unit) { (parentFragment as T).action() } -fun MatchResult?.parseMatchedInt(index: Int): Int? = this?.groups?.get(index)?.value?.toIntOrNull() \ No newline at end of file +fun MatchResult?.parseMatchedInt(index: Int): Int? = this?.groups?.get(index)?.value?.toIntOrNull() + +/** + * Adds items after the index meeting a [predicateIndex] + */ +fun MutableList.addAfterIndexInLine( + predicateIndex: (T) -> Boolean, + items: List +) { + val newIndex = indexOfFirst(predicateIndex) + if (newIndex in 0 until size) { + addAll(newIndex + 1, items) + } else { + addAll(size, items) + } +} + +/** + * Moves all items meeting a [predicateMove] after the index meeting a [predicateIndex] + */ +fun MutableList.moveAfterIndexInLine( + predicateIndex: (T) -> Boolean, + predicateMove: (T) -> Boolean +) { + val split = partition(predicateMove) + clear() + addAll(split.second) + val newIndex = indexOfFirst(predicateIndex) + if (newIndex in 0 until size) { + addAll(newIndex + 1, split.first) + } else { + addAll(size, split.first) + } +} \ No newline at end of file diff --git a/core-utils/src/test/java/com/anytypeio/anytype/UtilsTest.kt b/core-utils/src/test/java/com/anytypeio/anytype/UtilsTest.kt index e1b69bcdb2..6990cf5e79 100644 --- a/core-utils/src/test/java/com/anytypeio/anytype/UtilsTest.kt +++ b/core-utils/src/test/java/com/anytypeio/anytype/UtilsTest.kt @@ -1,5 +1,7 @@ package com.anytypeio.anytype +import com.anytypeio.anytype.core_utils.ext.addAfterIndexInLine +import com.anytypeio.anytype.core_utils.ext.moveAfterIndexInLine import com.anytypeio.anytype.core_utils.ext.shift import com.anytypeio.anytype.core_utils.ext.swap import org.junit.Assert.assertEquals @@ -41,4 +43,69 @@ class UtilsTest { .shift(4, 0) assertEquals(testList2, shiftList3) } + + @Test + fun `when predicate index is true, should move all elements after this index`() { + + list.moveAfterIndexInLine( + predicateIndex = { it == 9 }, + predicateMove = { i -> listOf(0, 1, 4, 36, 81).contains(i) } + ) + + val expected = mutableListOf(9, 0, 1, 4, 36, 81, 16, 25, 49, 64) + + assertEquals(expected, list) + } + + @Test + fun `when predicate index is false, should move all elements at the and`() { + + list.moveAfterIndexInLine( + predicateIndex = { it == 91 }, + predicateMove = { i -> listOf(0, 1, 4, 36, 81).contains(i) } + ) + + val expected = mutableListOf(9, 16, 25, 49, 64, 0, 1, 4, 36, 81) + + assertEquals(expected, list) + } + + @Test + fun `when predicate index is true and predicateMove is false, should not change the list`() { + + list.moveAfterIndexInLine( + predicateIndex = { it == 9 }, + predicateMove = { i -> listOf(10, 11, 14, 181).contains(i) } + ) + + val expected = mutableListOf(0, 1, 4, 9, 16, 25, 36, 49, 64, 81) + + assertEquals(expected, list) + } + + @Test + fun `when predicate index is true, should add elements after this index`() { + + list.addAfterIndexInLine( + predicateIndex = { it == 9 }, + items = listOf(10, 11, 120), + ) + + val expected = mutableListOf(0, 1, 4, 9, 10, 11, 120, 16, 25, 36, 49, 64, 81) + + assertEquals(expected, list) + } + + @Test + fun `when predicate index is false, should add all items at the end`() { + + list.addAfterIndexInLine( + predicateIndex = { it == 19 }, + items = listOf(10, 11, 120), + ) + + val expected = mutableListOf(0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 10, 11, 120) + + assertEquals(expected, list) + } } \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventChannel.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventChannel.kt index f57e20b085..f0f6fce47e 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventChannel.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventChannel.kt @@ -29,14 +29,12 @@ class MiddlewareEventChannel( msg.blockSetAlign, msg.blockSetDiv, msg.blockSetRelation, - //msg.blockDataviewRecordsSet, msg.blockDataviewRelationSet, - //msg.blockDataviewRecordsUpdate, msg.blockDataviewViewDelete, msg.blockDataviewViewSet, msg.objectRelationsAmend, msg.objectRelationsRemove, - //msg.objectRelationsSet + msg.blockDataviewViewUpdate ) return events.any { it != null } } diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventMapper.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventMapper.kt index cc47fb6b5a..733b93847c 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventMapper.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/MiddlewareEventMapper.kt @@ -225,5 +225,23 @@ fun anytype.Event.Message.toCoreModels( keys = event.relationKeys ) } + blockDataviewViewUpdate != null -> { + val event = blockDataviewViewUpdate + checkNotNull(event) + Event.Command.DataView.UpdateView( + context = context, + block = event.id, + viewerId = event.viewId, + filterUpdates = event.filter.mapNotNull { filter -> + filter.toCoreModels() + }, + sortUpdates = event.sort.mapNotNull { sort -> + sort.toCoreModels() + }, + relationUpdates = event.relation.mapNotNull { relation -> + relation.toCoreModels() + } + ) + } else -> null } \ No newline at end of file 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 da75f5909f..6bd84d6273 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 @@ -43,7 +43,9 @@ typealias MDVFilterOperator = anytype.model.Block.Content.Dataview.Filter.Operat typealias MDVRelation = anytype.model.Block.Content.Dataview.Relation typealias MDVDateFormat = anytype.model.Block.Content.Dataview.Relation.DateFormat typealias MDVTimeFormat = anytype.model.Block.Content.Dataview.Relation.TimeFormat - +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 MObjectType = anytype.model.ObjectType typealias MSmartBlockType = anytype.model.SmartBlockType diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt index 52d5851505..81e161db7e 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/EventMappers.kt @@ -2,9 +2,129 @@ package com.anytypeio.anytype.middleware.mappers import anytype.Event import com.anytypeio.anytype.core_models.SearchResult +import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVFilterUpdate +import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVSortUpdate +import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerRelationUpdate -fun Event.Object.Subscription.Counters.parse() : SearchResult.Counter = SearchResult.Counter( +fun Event.Object.Subscription.Counters.parse(): SearchResult.Counter = SearchResult.Counter( total = total.toInt(), prev = prevCount.toInt(), next = nextCount.toInt() -) \ No newline at end of file +) + +fun MDVFilterUpdate.toCoreModels(): DVFilterUpdate? { + val filter = this + if (filter.add != null) { + val add = filter.add + checkNotNull(add) + return DVFilterUpdate.Add( + filters = add.items.map { it.toCoreModels() }, + afterId = add.afterId + ) + } + if (filter.move != null) { + val move = filter.move + checkNotNull(move) + return DVFilterUpdate.Move( + afterId = move.afterId, + ids = move.ids, + ) + } + if (filter.remove != null) { + val remove = filter.remove + checkNotNull(remove) + return DVFilterUpdate.Remove( + ids = remove.ids, + ) + } + if (filter.update != null) { + val update = filter.update + checkNotNull(update) + val item = update.item + if (item != null) { + return DVFilterUpdate.Update( + id = update.id, + filter = item.toCoreModels() + ) + } + } + return null +} + +fun MDVSortUpdate.toCoreModels(): DVSortUpdate? { + val sort = this + if (sort.add != null) { + val add = sort.add + checkNotNull(add) + return DVSortUpdate.Add( + sorts = add.items.map { it.toCoreModels() }, + afterId = add.afterId + ) + } + if (sort.move != null) { + val move = sort.move + checkNotNull(move) + return DVSortUpdate.Move( + afterId = move.afterId, + ids = move.ids, + ) + } + if (sort.remove != null) { + val remove = sort.remove + checkNotNull(remove) + return DVSortUpdate.Remove( + ids = remove.ids, + ) + } + if (sort.update != null) { + val update = sort.update + checkNotNull(update) + val item = update.item + if (item != null) { + return DVSortUpdate.Update( + id = update.id, + sort = item.toCoreModels() + ) + } + } + return null +} + +fun MDVRelationUpdate.toCoreModels(): DVViewerRelationUpdate? { + val relation = this + if (relation.add != null) { + val add = relation.add + checkNotNull(add) + return DVViewerRelationUpdate.Add( + relations = add.items.map { it.toCoreModels() }, + afterId = add.afterId + ) + } + if (relation.move != null) { + val move = relation.move + checkNotNull(move) + return DVViewerRelationUpdate.Move( + afterId = move.afterId, + ids = move.ids, + ) + } + if (relation.remove != null) { + val remove = relation.remove + checkNotNull(remove) + return DVViewerRelationUpdate.Remove( + ids = remove.ids, + ) + } + if (relation.update != null) { + val update = relation.update + checkNotNull(update) + val item = update.item + if (item != null) { + return DVViewerRelationUpdate.Update( + id = update.id, + relation = item.toCoreModels() + ) + } + } + return null +} \ 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 d861beaafc..fcffcaaa4d 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 @@ -2,12 +2,17 @@ package com.anytypeio.anytype.presentation.sets import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.DV +import com.anytypeio.anytype.core_models.DVFilter import com.anytypeio.anytype.core_models.DVRecord +import com.anytypeio.anytype.core_models.DVSort +import com.anytypeio.anytype.core_models.DVViewerRelation import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.Relation import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_utils.ext.addAfterIndexInLine +import com.anytypeio.anytype.core_utils.ext.replace import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.presentation.editor.editor.model.BlockView import com.anytypeio.anytype.presentation.objects.ObjectIcon @@ -19,6 +24,11 @@ import com.anytypeio.anytype.presentation.relations.objectTypeRelation import com.anytypeio.anytype.presentation.relations.view import com.anytypeio.anytype.presentation.sets.model.ObjectView import com.anytypeio.anytype.presentation.sets.model.SimpleRelationView +import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVSortUpdate +import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVFilterUpdate +import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerRelationUpdate +import com.anytypeio.anytype.core_utils.ext.mapInPlace +import com.anytypeio.anytype.core_utils.ext.moveAfterIndexInLine fun ObjectSet.featuredRelations( ctx: Id, @@ -179,4 +189,97 @@ fun ObjectWrapper.Basic.toObjectView(urlBuilder: UrlBuilder): ObjectView = when types = type, isRelation = type.contains(ObjectTypeIds.RELATION) ) +} + +fun List.updateFilters(updates: List): List { + val filters = this.toMutableList() + updates.forEach { update -> + when (update) { + is DVFilterUpdate.Add -> { + filters.addAfterIndexInLine( + predicateIndex = { it.relationKey == update.afterId }, + items = update.filters + ) + } + is DVFilterUpdate.Move -> { + filters.moveAfterIndexInLine( + predicateIndex = { filter -> filter.relationKey == update.afterId }, + predicateMove = { filter -> update.ids.contains(filter.relationKey) } + ) + } + is DVFilterUpdate.Remove -> { + filters.retainAll { + !update.ids.contains(it.relationKey) + } + } + is DVFilterUpdate.Update -> { + filters.mapInPlace { filter -> + if (filter.relationKey == update.id) update.filter else filter + } + } + } + } + return filters +} + +fun List.updateSorts(updates: List): List { + val sorts = this.toMutableList() + updates.forEach { update -> + when (update) { + is DVSortUpdate.Add -> { + sorts.addAfterIndexInLine( + predicateIndex = { it.relationKey == update.afterId }, + items = update.sorts + ) + } + is DVSortUpdate.Move -> { + sorts.moveAfterIndexInLine( + predicateIndex = { sort -> sort.relationKey == update.afterId }, + predicateMove = { sort -> update.ids.contains(sort.relationKey) } + ) + } + is DVSortUpdate.Remove -> { + sorts.retainAll { + !update.ids.contains(it.relationKey) + } + } + is DVSortUpdate.Update -> { + sorts.mapInPlace { sort -> + if (sort.relationKey == update.id) update.sort else sort + } + } + } + } + return sorts +} + +fun List.updateViewerRelations(updates: List): List { + val relations = this.toMutableList() + updates.forEach { update -> + when (update) { + is DVViewerRelationUpdate.Add -> { + relations.addAfterIndexInLine( + predicateIndex = { it.key == update.afterId }, + items = update.relations + ) + } + is DVViewerRelationUpdate.Move -> { + relations.moveAfterIndexInLine( + predicateIndex = { relation -> relation.key == update.afterId }, + predicateMove = { relation -> update.ids.contains(relation.key) } + ) + } + is DVViewerRelationUpdate.Remove -> { + relations.retainAll { + !update.ids.contains(it.key) + } + } + is DVViewerRelationUpdate.Update -> { + relations.mapInPlace { relation -> + if (relation.key == update.id) update.relation else relation + } + } + } + } + return relations } \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducer.kt index c955194bae..47ce186973 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducer.kt @@ -1,5 +1,6 @@ package com.anytypeio.anytype.presentation.sets +import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.DV import com.anytypeio.anytype.core_models.Event import com.anytypeio.anytype.core_models.Event.Command @@ -146,6 +147,53 @@ class ObjectSetReducer { } ) } + is Command.DataView.UpdateView -> { + val updatedBlocks = state.blocks.map { block: Block -> + val content = block.content + if (block.id == event.block && content is DV) { + block.copy( + content = content.copy( + viewers = content.viewers.map { viewer -> + if (viewer.id == event.viewerId) { + val filters = if (event.filterUpdates.isNotEmpty()) { + viewer.filters.updateFilters( + updates = event.filterUpdates + ) + } else { + viewer.filters + } + val sorts = if (event.sortUpdates.isNotEmpty()) { + viewer.sorts.updateSorts( + updates = event.sortUpdates + ) + } else { + viewer.sorts + } + val viewerRelations = + if (event.relationUpdates.isNotEmpty()) { + viewer.viewerRelations.updateViewerRelations( + updates = event.relationUpdates + ) + } else { + viewer.viewerRelations + } + viewer.copy( + filters = filters, + sorts = sorts, + viewerRelations = viewerRelations + ) + } else { + viewer + } + } + ) + ) + } else { + block + } + } + state.copy(blocks = updatedBlocks) + } is Command.Details.Set -> { state.copy( details = state.details.toMutableMap().apply { diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt index c9832c1fd9..d197b8828a 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt @@ -1,8 +1,15 @@ package com.anytypeio.anytype.presentation.sets import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.DVSort import com.anytypeio.anytype.core_models.Event import com.anytypeio.anytype.core_models.Relation +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.StubFilter +import com.anytypeio.anytype.core_models.StubSort +import com.anytypeio.anytype.core_models.StubTitle import com.anytypeio.anytype.test_utils.MockDataFactory import org.junit.Before import org.junit.Test @@ -361,4 +368,322 @@ class ObjectSetReducerTest { assertEquals(expected, result) } + + @Test + fun `when getting add, move, update, remove sorts events, should proper update viewer`() { + + val context = MockDataFactory.randomUuid() + val title = StubTitle() + + val relationKey1 = MockDataFactory.randomUuid() + val relationKey2 = MockDataFactory.randomUuid() + val relationKey3 = MockDataFactory.randomUuid() + val relationKey4 = MockDataFactory.randomUuid() + val relationKey5 = MockDataFactory.randomUuid() + val sort1 = StubSort(relationKey = relationKey1) + val sort2 = StubSort(relationKey = relationKey2) + val sort3 = StubSort(relationKey = relationKey3) + val sort4 = StubSort(relationKey = relationKey4) + val sort5 = StubSort(relationKey = relationKey5) + + val viewer1 = StubDataViewView( + type = Block.Content.DataView.Viewer.Type.BOARD, + sorts = listOf(sort1, sort2, sort3, sort4) + ) + val viewer2 = StubDataViewView( + type = Block.Content.DataView.Viewer.Type.GRID, + sorts = listOf(sort1) + ) + val dataView = StubDataView( + id = MockDataFactory.randomUuid(), + views = listOf(viewer1, viewer2), + sources = listOf(MockDataFactory.randomString()) + ) + + val blocks = listOf(title, dataView) + + val objectSet = ObjectSet(blocks = blocks) + + // TESTING + + val event = Event.Command.DataView.UpdateView( + context = context, + block = dataView.id, + viewerId = viewer1.id, + sortUpdates = listOf( + Event.Command.DataView.UpdateView.DVSortUpdate.Move( + afterId = relationKey3, + ids = listOf(relationKey1, relationKey2) + ), + Event.Command.DataView.UpdateView.DVSortUpdate.Add( + afterId = relationKey3, + sorts = listOf(sort5) + ), + Event.Command.DataView.UpdateView.DVSortUpdate.Update( + id = relationKey2, + sort = DVSort(relationKey2, Block.Content.DataView.Sort.Type.DESC) + ), + Event.Command.DataView.UpdateView.DVSortUpdate.Remove( + ids = listOf(relationKey1) + ) + ), + filterUpdates = listOf(), + relationUpdates = listOf() + ) + + val result = reducer.reduce(state = objectSet, events = listOf(event)) + + val expectedSorts = listOf( + DVSort( + relationKey = relationKey3, + type = Block.Content.DataView.Sort.Type.ASC + ), + DVSort( + relationKey = relationKey5, + type = Block.Content.DataView.Sort.Type.ASC + ), + DVSort( + relationKey = relationKey2, + type = Block.Content.DataView.Sort.Type.DESC + ), + DVSort( + relationKey = relationKey4, + type = Block.Content.DataView.Sort.Type.ASC + ) + ) + val expectedDataView = Block( + id = dataView.id, + content = Block.Content.DataView( + sources = (dataView.content as Block.Content.DataView).sources, + viewers = listOf( + Block.Content.DataView.Viewer( + id = viewer1.id, + name = viewer1.name, + type = viewer1.type, + viewerRelations = viewer1.viewerRelations, + sorts = expectedSorts, + filters = viewer1.filters + ), + Block.Content.DataView.Viewer( + id = viewer2.id, + name = viewer2.name, + type = Block.Content.DataView.Viewer.Type.GRID, + viewerRelations = viewer2.viewerRelations, + sorts = listOf(sort1), + filters = listOf() + ) + ), + relations = listOf() + ), + fields = Block.Fields.empty(), + children = listOf() + ) + + val expected = ObjectSetReducer.Transformation( + state = ObjectSet(blocks = listOf(title, expectedDataView)), + effects = emptyList() + ) + + assertEquals(expected, result) + } + + @Test + fun `when getting add, move, update, remove filters events, should proper update viewer`() { + + val context = MockDataFactory.randomUuid() + val title = StubTitle() + + val relationKey1 = MockDataFactory.randomUuid() + val relationKey2 = MockDataFactory.randomUuid() + val relationKey3 = MockDataFactory.randomUuid() + val relationKey4 = MockDataFactory.randomUuid() + val relationKey5 = MockDataFactory.randomUuid() + val filter1 = StubFilter(relationKey = relationKey1) + val filter2 = StubFilter(relationKey = relationKey2) + val filter3 = StubFilter(relationKey = relationKey3) + val filter4 = StubFilter(relationKey = relationKey4) + val filter5 = StubFilter(relationKey = relationKey5) + + val viewer1 = StubDataViewView( + type = Block.Content.DataView.Viewer.Type.BOARD, + filters = listOf(filter1, filter2, filter3, filter4) + ) + val dataView = StubDataView( + id = MockDataFactory.randomUuid(), + views = listOf(viewer1), + sources = listOf(MockDataFactory.randomString()) + ) + + val blocks = listOf(title, dataView) + + val objectSet = ObjectSet(blocks = blocks) + + // TESTING + + val filter2Value = MockDataFactory.randomString() + val event = Event.Command.DataView.UpdateView( + context = context, + block = dataView.id, + viewerId = viewer1.id, + sortUpdates = listOf(), + filterUpdates = listOf( + Event.Command.DataView.UpdateView.DVFilterUpdate.Move( + afterId = relationKey3, + ids = listOf(relationKey1, relationKey2) + ), + Event.Command.DataView.UpdateView.DVFilterUpdate.Add( + afterId = relationKey3, + filters = listOf(filter5) + ), + Event.Command.DataView.UpdateView.DVFilterUpdate.Update( + id = relationKey2, + filter = filter2.copy( + value = filter2Value + ) + ), + Event.Command.DataView.UpdateView.DVFilterUpdate.Remove( + ids = listOf(relationKey1) + ) + ), + relationUpdates = listOf() + ) + + val result = reducer.reduce(state = objectSet, events = listOf(event)) + + val expectedFilters = listOf( + filter3, + filter5, + filter2.copy( + value = filter2Value + ), + filter4 + ) + + val expectedDataView = Block( + id = dataView.id, + content = Block.Content.DataView( + sources = (dataView.content as Block.Content.DataView).sources, + viewers = listOf( + Block.Content.DataView.Viewer( + id = viewer1.id, + name = viewer1.name, + type = viewer1.type, + viewerRelations = viewer1.viewerRelations, + sorts = viewer1.sorts, + filters = expectedFilters + ) + ), + relations = listOf() + ), + fields = Block.Fields.empty(), + children = listOf() + ) + + val expected = ObjectSetReducer.Transformation( + state = ObjectSet(blocks = listOf(title, expectedDataView)), + effects = emptyList() + ) + + assertEquals(expected, result) + } + + @Test + fun `when getting add, move, update, remove relations events, should proper update viewer`() { + + val context = MockDataFactory.randomUuid() + val title = StubTitle() + + val relationKey1 = MockDataFactory.randomUuid() + val relationKey2 = MockDataFactory.randomUuid() + val relationKey3 = MockDataFactory.randomUuid() + val relationKey4 = MockDataFactory.randomUuid() + val relationKey5 = MockDataFactory.randomUuid() + val relation1 = StubDataViewViewRelation(key = relationKey1) + val relation2 = StubDataViewViewRelation(key = relationKey2) + val relation3 = StubDataViewViewRelation(key = relationKey3) + val relation4 = StubDataViewViewRelation(key = relationKey4) + val relation5 = StubDataViewViewRelation(key = relationKey5) + + val viewer1 = StubDataViewView( + type = Block.Content.DataView.Viewer.Type.BOARD, + viewerRelations = listOf(relation1, relation2, relation3, relation4) + ) + val dataView = StubDataView( + id = MockDataFactory.randomUuid(), + views = listOf(viewer1), + sources = listOf(MockDataFactory.randomString()) + ) + + val blocks = listOf(title, dataView) + + val objectSet = ObjectSet(blocks = blocks) + + // TESTING + + val relation2IsVisible = !relation2.isVisible + val event = Event.Command.DataView.UpdateView( + context = context, + block = dataView.id, + viewerId = viewer1.id, + sortUpdates = listOf(), + filterUpdates = listOf(), + relationUpdates = listOf( + Event.Command.DataView.UpdateView.DVViewerRelationUpdate.Move( + afterId = relationKey3, + ids = listOf(relationKey1, relationKey2) + ), + Event.Command.DataView.UpdateView.DVViewerRelationUpdate.Add( + afterId = relationKey3, + relations = listOf(relation5) + ), + Event.Command.DataView.UpdateView.DVViewerRelationUpdate.Update( + id = relationKey2, + relation = relation2.copy( + isVisible = relation2IsVisible + ) + ), + Event.Command.DataView.UpdateView.DVViewerRelationUpdate.Remove( + ids = listOf(relationKey1) + ) + ) + ) + + val result = reducer.reduce(state = objectSet, events = listOf(event)) + + val expectedRelations = listOf( + relation3, + relation5, + relation2.copy( + isVisible = relation2IsVisible + ), + relation4 + ) + + val expectedDataView = Block( + id = dataView.id, + content = Block.Content.DataView( + sources = (dataView.content as Block.Content.DataView).sources, + viewers = listOf( + Block.Content.DataView.Viewer( + id = viewer1.id, + name = viewer1.name, + type = viewer1.type, + viewerRelations = expectedRelations, + sorts = viewer1.sorts, + filters = viewer1.filters + ) + ), + relations = listOf() + ), + fields = Block.Fields.empty(), + children = listOf() + ) + + val expected = ObjectSetReducer.Transformation( + state = ObjectSet(blocks = listOf(title, expectedDataView)), + effects = emptyList() + ) + + assertEquals(expected, result) + } } \ No newline at end of file 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 521bd84a1c..695536451a 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 @@ -41,4 +41,29 @@ fun StubDataViewViewRelation( ) : DVViewerRelation = DVViewerRelation( key = key, isVisible = isVisible +) + +fun StubSort( + relationKey: Key = MockDataFactory.randomUuid(), + type: DVSortType = DVSortType.ASC +): DVSort = DVSort( + relationKey = relationKey, + type = type +) + +fun StubFilter( + relationKey: Key = MockDataFactory.randomUuid(), + relationFormat: RelationFormat = RelationFormat.LONG_TEXT, + operator: DVFilterOperator = DVFilterOperator.AND, + condition: DVFilterCondition = DVFilterCondition.EQUAL, + quickOption: DVFilterQuickOption = DVFilterQuickOption.EXACT_DATE, + value: Any? = null + +): DVFilter = DVFilter( + relationKey = relationKey, + relationFormat = relationFormat, + operator = operator, + condition = condition, + quickOption = quickOption, + value = value ) \ No newline at end of file