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:
parent
efca8dbeb9
commit
f11b8b9be8
5 changed files with 259 additions and 5 deletions
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue