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

DROID-1351 Widgets | Enhancement | Integrate middleware commands for granular widget updates (#99)

This commit is contained in:
Evgenii Kozlov 2023-06-26 20:10:30 +02:00 committed by GitHub
parent efca8dbeb9
commit f11b8b9be8
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 259 additions and 5 deletions

View file

@ -368,7 +368,8 @@ data class Block(
data class Widget(
val layout: Layout,
val limit: Int = 0
val limit: Int = 0,
val activeView: Id? = null
) : Content() {
enum class Layout {
TREE, LINK, LIST, COMPACT_LIST

View file

@ -83,10 +83,10 @@ sealed class Event {
override val context: String,
val id: Id,
val target: Id,
val iconSize: Block.Content.Link.IconSize?,
val cardStyle: Block.Content.Link.CardStyle?,
val description: Block.Content.Link.Description?,
val relations: Set<Key>?,
val iconSize: Block.Content.Link.IconSize? = null,
val cardStyle: Block.Content.Link.CardStyle? = null,
val description: Block.Content.Link.Description? = null,
val relations: Set<Key>? = null,
) : Command()
/**
@ -221,6 +221,16 @@ sealed class Event {
) : ObjectRelation()
}
sealed class Widgets: Command() {
data class SetWidget(
override val context: Id,
val widget: Id,
val layout: WidgetLayout? = null,
val activeView: Id? = null,
val limit: Int? = null
) : Widgets()
}
sealed class DataView : Command() {
/**

View file

@ -1,6 +1,8 @@
package com.anytypeio.anytype.middleware.interactor
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.middleware.mappers.MWidgetLayout
import com.anytypeio.anytype.middleware.mappers.toCoreModel
import com.anytypeio.anytype.middleware.mappers.toCoreModels
import com.anytypeio.anytype.middleware.mappers.toCoreModelsAlign
@ -167,6 +169,23 @@ fun anytype.Event.Message.toCoreModels(
style = style.value_.toCoreModels()
)
}
blockSetWidget != null -> {
val event = blockSetWidget
checkNotNull(event)
Event.Command.Widgets.SetWidget(
context = context,
widget = event.id,
activeView = event.viewId?.value_,
limit = event.limit?.value_,
layout = when(event.layout?.value_) {
MWidgetLayout.Link -> Block.Content.Widget.Layout.LINK
MWidgetLayout.Tree -> Block.Content.Widget.Layout.TREE
MWidgetLayout.List -> Block.Content.Widget.Layout.LIST
MWidgetLayout.CompactList -> Block.Content.Widget.Layout.COMPACT_LIST
else -> null
}
)
}
blockDataviewViewSet != null -> {
val event = blockDataviewViewSet
checkNotNull(event)

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Config
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Id
@ -757,6 +758,48 @@ class HomeScreenViewModel(
is Event.Command.Details -> {
curr = curr.copy(details = curr.details.process(e))
}
is Event.Command.LinkGranularChange -> {
curr = curr.copy(
blocks = curr.blocks.map { block ->
if (block.id == e.id) {
val content = block.content
if (content is Block.Content.Link) {
block.copy(
content = content.copy(
target = e.target
)
)
} else {
block
}
} else {
block
}
}
)
}
is Event.Command.Widgets.SetWidget -> {
curr = curr.copy(
blocks = curr.blocks.map { block ->
if (block.id == e.widget) {
val content = block.content
if (content is Block.Content.Widget) {
block.copy(
content = content.copy(
layout = e.layout ?: content.layout,
limit = e.limit ?: content.limit,
activeView = e.activeView ?: content.activeView
)
)
} else {
block
}
} else {
block
}
}
)
}
else -> {
Timber.d("Skipping event: $e")
}

View file

@ -7,6 +7,7 @@ import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectTypeIds
import com.anytypeio.anytype.core_models.ObjectView
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Payload
@ -2041,6 +2042,186 @@ class HomeScreenViewModelTest {
}
}
@Test
fun `should react to change-widget-source event when source type is page for old and new source`() = runTest {
val currentSourceObject = StubObject(
id = "SOURCE OBJECT 1",
links = emptyList(),
objectType = ObjectTypeIds.PAGE
)
val newSourceObject = StubObject(
id = "SOURCE OBJECT 2",
links = emptyList(),
objectType = ObjectTypeIds.PAGE
)
val sourceLink = StubLinkToObjectBlock(
id = "SOURCE LINK",
target = currentSourceObject.id
)
val widgetBlock = StubWidgetBlock(
id = "WIDGET BLOCK",
layout = Block.Content.Widget.Layout.TREE,
children = listOf(sourceLink.id)
)
val smartBlock = StubSmartBlock(
id = WIDGET_OBJECT_ID,
children = listOf(widgetBlock.id),
)
val givenObjectView = StubObjectView(
root = WIDGET_OBJECT_ID,
blocks = listOf(
smartBlock,
widgetBlock,
sourceLink
),
details = mapOf(
currentSourceObject.id to currentSourceObject.map
)
)
stubConfig()
stubInterceptEvents(
events = flow {
delay(300)
emit(
listOf(
Event.Command.LinkGranularChange(
context = WIDGET_OBJECT_ID,
id = sourceLink.id,
target = newSourceObject.id
)
)
)
}
)
stubOpenObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
)
stubCollapsedWidgetState(any())
stubGetWidgetSession()
val vm = buildViewModel()
// TESTING
vm.onStart()
vm.views.test {
val firstTimeState = awaitItem()
assertEquals(
actual = firstTimeState,
expected = emptyList()
)
delay(1)
val secondTimeItem = awaitItem()
assertTrue {
val firstWidget = secondTimeItem.first()
firstWidget is WidgetView.Tree && firstWidget.source.id == currentSourceObject.id
}
val thirdTimeItem = awaitItem()
advanceUntilIdle()
assertTrue {
val firstWidget = thirdTimeItem.first()
firstWidget is WidgetView.Tree && firstWidget.source.id == newSourceObject.id
}
}
}
@Test
fun `should react to change-widget-layout event when tree changed to link`() = runTest {
val currentSourceObject = StubObject(
id = "SOURCE OBJECT 1",
links = emptyList(),
objectType = ObjectTypeIds.PAGE
)
val sourceLink = StubLinkToObjectBlock(
id = "SOURCE LINK",
target = currentSourceObject.id
)
val widgetBlock = StubWidgetBlock(
id = "WIDGET BLOCK",
layout = Block.Content.Widget.Layout.TREE,
children = listOf(sourceLink.id)
)
val smartBlock = StubSmartBlock(
id = WIDGET_OBJECT_ID,
children = listOf(widgetBlock.id),
)
val givenObjectView = StubObjectView(
root = WIDGET_OBJECT_ID,
blocks = listOf(
smartBlock,
widgetBlock,
sourceLink
),
details = mapOf(
currentSourceObject.id to currentSourceObject.map
)
)
stubConfig()
stubInterceptEvents(
events = flow {
delay(300)
emit(
listOf(
Event.Command.Widgets.SetWidget(
context = WIDGET_OBJECT_ID,
widget = widgetBlock.id,
layout = Block.Content.Widget.Layout.LINK,
)
)
)
}
)
stubOpenObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
)
stubCollapsedWidgetState(any())
stubGetWidgetSession()
val vm = buildViewModel()
// TESTING
vm.onStart()
vm.views.test {
val firstTimeState = awaitItem()
assertEquals(
actual = firstTimeState,
expected = emptyList()
)
delay(1)
val secondTimeItem = awaitItem()
assertTrue {
val firstWidget = secondTimeItem.first()
firstWidget is WidgetView.Tree
}
val thirdTimeItem = awaitItem()
advanceUntilIdle()
assertTrue {
val firstWidget = thirdTimeItem.first()
firstWidget is WidgetView.Link
}
}
}
private fun stubInterceptEvents(events: Flow<List<Event>>) {
interceptEvents.stub {
on { build(InterceptEvents.Params(WIDGET_OBJECT_ID)) } doReturn events