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

DROID-1368 Widgets | Enhancement | Syncing active view (#106)

This commit is contained in:
Evgenii Kozlov 2023-06-28 15:12:39 +02:00 committed by GitHub
parent 4df10d09d2
commit ee42888ea9
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 441 additions and 38 deletions

View file

@ -14,7 +14,7 @@ class DefaultFeatureToggles @Inject constructor(
private val buildProvider: BuildProvider
) : FeatureToggles {
override val isLogFromMiddlewareLibrary =
override val isLogFromGoProcess =
BuildConfig.LOG_FROM_MW_LIBRARY && buildProvider.isDebug()
override val isLogMiddlewareInteraction =
@ -32,4 +32,5 @@ class DefaultFeatureToggles @Inject constructor(
override val isAutoUpdateEnabled: Boolean = false
override val isConciseLogging: Boolean = true
}

View file

@ -4,10 +4,12 @@ interface FeatureToggles {
val isAutoUpdateEnabled: Boolean
val isLogFromMiddlewareLibrary: Boolean
val isLogFromGoProcess: Boolean
val isLogMiddlewareInteraction: Boolean
val isConciseLogging: Boolean
val excludeThreadStatusLogging: Boolean
val isLogEditorViewModelEvents: Boolean

View file

@ -764,6 +764,16 @@ class BlockDataRepository(
type = type
)
override suspend fun setWidgetViewId(
ctx: Id,
widget: Id,
view: Id
): Payload = remote.setWidgetViewId(
ctx = ctx,
widget = widget,
view = view
)
override suspend fun addDataViewFilter(command: Command.AddFilter): Payload {
return remote.addDataViewFilter(command = command)
}

View file

@ -337,6 +337,12 @@ interface BlockRemote {
type: Block.Content.Widget.Layout
): Payload
suspend fun setWidgetViewId(
ctx: Id,
widget: Id,
view: Id
): Payload
suspend fun addDataViewFilter(command: Command.AddFilter): Payload
suspend fun removeDataViewFilter(command: Command.RemoveFilter): Payload
suspend fun replaceDataViewFilter(command: Command.ReplaceFilter): Payload

View file

@ -88,7 +88,7 @@ abstract class ResultInteractor<in P, R>(
suspend fun run(params: P) = doWork(params)
/*
* Executes on the caller's thread.
* N.B. Executes on the caller's thread.
* */
suspend fun execute(params: P): Resultat<R> = runCatchingL { doWork(params) }

View file

@ -389,6 +389,12 @@ interface BlockRepository {
type: Block.Content.Widget.Layout
): Payload
suspend fun setWidgetViewId(
ctx: Id,
widget: Id,
view: Id
): Payload
suspend fun addDataViewFilter(command: Command.AddFilter): Payload
suspend fun removeDataViewFilter(command: Command.RemoveFilter): Payload
suspend fun replaceDataViewFilter(command: Command.ReplaceFilter): Payload

View file

@ -0,0 +1,29 @@
package com.anytypeio.anytype.domain.widgets
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import javax.inject.Inject
/**
* Use-case for setting active view id for widget with list or compact list layout.
*/
class SetWidgetActiveView @Inject constructor(
private val repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<SetWidgetActiveView.Params, Payload>(dispatchers.io) {
override suspend fun doWork(params: Params) = repo.setWidgetViewId(
ctx = params.ctx,
widget = params.widget,
view = params.view
)
data class Params(
val ctx: Id,
val widget: Id,
val view: Id
)
}

View file

@ -728,6 +728,16 @@ class BlockMiddleware(
type = type
)
override suspend fun setWidgetViewId(
ctx: Id,
widget: Id,
view: Id
): Payload = middleware.setWidgetViewId(
ctx = ctx,
widget = widget,
view = view
)
override suspend fun addDataViewFilter(command: Command.AddFilter): Payload {
return middleware.addDataViewFilter(command)
}

View file

@ -1957,6 +1957,22 @@ class Middleware(
return response.event.toPayload()
}
fun setWidgetViewId(
ctx: Id,
widget: Id,
view: Id
): Payload {
val request = Rpc.BlockWidget.SetViewId.Request(
contextId = ctx,
blockId = widget,
viewId = view
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.blockWidgetSetViewId(request)
if (BuildConfig.DEBUG) logResponse(response)
return response.event.toPayload()
}
@Throws(Exception::class)
fun updateWidget(
ctx: Id,

View file

@ -38,7 +38,8 @@ class MiddlewareEventChannel(
msg.objectRelationsRemove,
msg.blockDataviewViewUpdate,
msg.blockDataviewTargetObjectIdSet,
msg.blockDataviewIsCollectionSet
msg.blockDataviewIsCollectionSet,
msg.blockSetWidget
)
return events.any { it != null }
}

View file

@ -47,9 +47,13 @@ interface MiddlewareProtobufLogger {
}
private fun Any.toLogMessage(): String {
return "${this::class.java.canonicalName}:\n${
protobufConverter.provideConverter().toJson(this)
}"
return if (featureToggles.isConciseLogging) {
this::class.java.canonicalName
} else {
"${this::class.java.canonicalName}:\n${
protobufConverter.provideConverter().toJson(this)
}"
}
}
private fun containsOnlyThreadStatusEvents(event: Event) : Boolean {

View file

@ -393,7 +393,8 @@ fun MBlock.toCoreWidget(): Block.Content.Widget {
MWidgetLayout.List -> Block.Content.Widget.Layout.LIST
MWidgetLayout.CompactList -> Block.Content.Widget.Layout.COMPACT_LIST
},
limit = content.limit
limit = content.limit,
activeView = content.viewId.ifEmpty { null }
)
}

View file

@ -427,6 +427,9 @@ interface MiddlewareService {
@Throws(Exception::class)
fun blockCreateWidget(request: Rpc.Block.CreateWidget.Request): Rpc.Block.CreateWidget.Response
@Throws(Exception::class)
fun blockWidgetSetViewId(request: Rpc.BlockWidget.SetViewId.Request) : Rpc.BlockWidget.SetViewId.Response
//endregion
//region WORKSPACE

View file

@ -17,7 +17,7 @@ class MiddlewareServiceImplementation @Inject constructor(
) : MiddlewareService {
init {
if (!featureToggles.isLogFromMiddlewareLibrary) {
if (!featureToggles.isLogFromGoProcess) {
Service.setEnv("ANYTYPE_LOG_LEVEL", "*=fatal;anytype*=error")
}
}
@ -164,6 +164,19 @@ class MiddlewareServiceImplementation @Inject constructor(
}
}
override fun blockWidgetSetViewId(request: Rpc.BlockWidget.SetViewId.Request): Rpc.BlockWidget.SetViewId.Response {
val encoded = Service.blockWidgetSetViewId(
Rpc.BlockWidget.SetViewId.Request.ADAPTER.encode(request)
)
val response = Rpc.BlockWidget.SetViewId.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != Rpc.BlockWidget.SetViewId.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
override fun blockDataViewActiveSet(request: Rpc.BlockDataview.View.SetActive.Request): Rpc.BlockDataview.View.SetActive.Response {
val encoded = Service.blockDataviewViewSetActive(
Rpc.BlockDataview.View.SetActive.Request.ADAPTER.encode(request)

View file

@ -43,6 +43,7 @@ import com.anytypeio.anytype.domain.widgets.CreateWidget
import com.anytypeio.anytype.domain.widgets.DeleteWidget
import com.anytypeio.anytype.domain.widgets.GetWidgetSession
import com.anytypeio.anytype.domain.widgets.SaveWidgetSession
import com.anytypeio.anytype.domain.widgets.SetWidgetActiveView
import com.anytypeio.anytype.domain.widgets.UpdateWidget
import com.anytypeio.anytype.presentation.extension.sendAddWidgetEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEvent
@ -74,6 +75,7 @@ import com.anytypeio.anytype.presentation.widgets.WidgetSessionStateHolder
import com.anytypeio.anytype.presentation.widgets.WidgetView
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
import com.anytypeio.anytype.presentation.widgets.getActiveTabViews
import com.anytypeio.anytype.presentation.widgets.parseActiveViews
import com.anytypeio.anytype.presentation.widgets.parseWidgets
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
@ -128,7 +130,8 @@ class HomeScreenViewModel(
private val saveWidgetSession: SaveWidgetSession,
private val spaceGradientProvider: SpaceGradientProvider,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val objectWatcher: ObjectWatcher
private val objectWatcher: ObjectWatcher,
private val setWidgetActiveView: SetWidgetActiveView
) : NavigationViewModel<HomeScreenViewModel.Navigation>(),
Reducer<ObjectView, Payload>,
WidgetActiveViewStateHolder by widgetActiveViewStateHolder,
@ -269,7 +272,7 @@ class HomeScreenViewModel(
root = state.obj.root,
details = state.obj.details
).also {
// TODO active view logic here?
widgetActiveViewStateHolder.init(state.obj.blocks.parseActiveViews())
}
}.collect {
Timber.d("Emitting list of widgets: ${it.size}")
@ -333,7 +336,7 @@ class HomeScreenViewModel(
SaveWidgetSession.Params(
WidgetSession(
collapsed = collapsedWidgetStateHolder.get(),
widgetsToActiveViews = views.value.getActiveTabViews()
widgetsToActiveViews = emptyMap()
)
)
)
@ -783,6 +786,7 @@ class HomeScreenViewModel(
)
}
is Event.Command.Widgets.SetWidget -> {
Timber.d("Set widget event: $e")
curr = curr.copy(
blocks = curr.blocks.map { block ->
if (block.id == e.widget) {
@ -844,7 +848,6 @@ class HomeScreenViewModel(
}
if (session != null) {
collapsedWidgetStateHolder.set(session.collapsed)
widgetActiveViewStateHolder.init(session.widgetsToActiveViews)
}
proceedWithOpeningWidgetObject(widgetObject = configStorage.get().widgets)
}
@ -1053,6 +1056,28 @@ class HomeScreenViewModel(
}
}
override fun onChangeCurrentWidgetView(widget: Id, view: Id) {
widgetActiveViewStateHolder.onChangeCurrentWidgetView(
widget = widget,
view = view
).also {
viewModelScope.launch {
setWidgetActiveView.stream(
SetWidgetActiveView.Params(
ctx = configStorage.get().widgets,
widget = widget,
view = view,
)
).collect { result ->
result.fold(
onSuccess = { objectPayloadDispatcher.send(it) },
onFailure = { Timber.e(it, "Error while updating active view") }
)
}
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.launch {
@ -1094,7 +1119,8 @@ class HomeScreenViewModel(
private val saveWidgetSession: SaveWidgetSession,
private val spaceGradientProvider: SpaceGradientProvider,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val objectWatcher: ObjectWatcher
private val objectWatcher: ObjectWatcher,
private val setWidgetActiveView: SetWidgetActiveView
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = HomeScreenViewModel(
@ -1125,7 +1151,8 @@ class HomeScreenViewModel(
saveWidgetSession = saveWidgetSession,
spaceGradientProvider = spaceGradientProvider,
storeOfObjectTypes = storeOfObjectTypes,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
setWidgetActiveView = setWidgetActiveView
) as T
}

View file

@ -77,6 +77,20 @@ sealed class Widget {
}
}
fun List<Block>.parseActiveViews() : WidgetToActiveView {
val result = mutableMapOf<WidgetId, WidgetActiveViewId>()
forEach { block ->
val content = block.content
if (content is Block.Content.Widget) {
val view = content.activeView
if (!view.isNullOrEmpty()) {
result[block.id] = view
}
}
}
return result
}
fun List<Block>.parseWidgets(
root: Id,
details: Map<Id, Struct>

View file

@ -1,10 +1,12 @@
package com.anytypeio.anytype.presentation.widgets
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_utils.tools.toPrettyString
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.mapLatest
import timber.log.Timber
interface WidgetActiveViewStateHolder {
@ -16,6 +18,7 @@ interface WidgetActiveViewStateHolder {
private val widgetToActiveView = MutableStateFlow<WidgetToActiveView>(mapOf())
override fun init(map: WidgetToActiveView) {
Timber.d("Initializing active view: ${map.toPrettyString()}")
widgetToActiveView.value = map
}
@ -29,4 +32,5 @@ interface WidgetActiveViewStateHolder {
}
}
typealias WidgetToActiveView = Map<Id, Id>
typealias WidgetToActiveView = Map<Id, Id>
typealias WidgetActiveViewId = Id

View file

@ -3,6 +3,9 @@ package com.anytypeio.anytype.presentation.home
import app.cash.turbine.test
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.DVFilter
import com.anytypeio.anytype.core_models.DVFilterCondition
import com.anytypeio.anytype.core_models.DVViewer
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
@ -13,6 +16,9 @@ import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubConfig
import com.anytypeio.anytype.core_models.StubDataView
import com.anytypeio.anytype.core_models.StubDataViewView
import com.anytypeio.anytype.core_models.StubFilter
import com.anytypeio.anytype.core_models.StubLinkToObjectBlock
import com.anytypeio.anytype.core_models.StubObject
import com.anytypeio.anytype.core_models.StubObjectView
@ -42,15 +48,16 @@ import com.anytypeio.anytype.domain.widgets.CreateWidget
import com.anytypeio.anytype.domain.widgets.DeleteWidget
import com.anytypeio.anytype.domain.widgets.GetWidgetSession
import com.anytypeio.anytype.domain.widgets.SaveWidgetSession
import com.anytypeio.anytype.domain.widgets.SetWidgetActiveView
import com.anytypeio.anytype.domain.widgets.UpdateWidget
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.search.Subscriptions
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.presentation.widgets.BundledWidgetSourceIds
import com.anytypeio.anytype.presentation.widgets.CollapsedWidgetStateHolder
import com.anytypeio.anytype.presentation.widgets.DataViewListWidgetContainer
import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction
import com.anytypeio.anytype.presentation.widgets.ListWidgetContainer
import com.anytypeio.anytype.presentation.widgets.TreeWidgetContainer
@ -81,6 +88,7 @@ import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyBlocking
import org.mockito.kotlin.verifyNoMoreInteractions
class HomeScreenViewModelTest {
@ -153,6 +161,9 @@ class HomeScreenViewModelTest {
@Mock
lateinit var getWidgetSession: GetWidgetSession
@Mock
lateinit var setWidgetActiveView: SetWidgetActiveView
@Mock
lateinit var storeOfObjectTypes: StoreOfObjectTypes
@ -205,7 +216,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events)
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubCollapsedWidgetState(any())
stubGetWidgetSession()
@ -278,7 +289,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubCollapsedWidgetState(any())
stubGetWidgetSession()
@ -349,7 +360,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -442,7 +453,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
@ -552,7 +563,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
@ -649,7 +660,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
@ -764,7 +775,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubFavoritesObjectWatcher()
stubSearchByIds(
@ -956,7 +967,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -1036,7 +1047,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -1175,7 +1186,7 @@ class HomeScreenViewModelTest {
}
)
stubConfig()
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -1242,7 +1253,7 @@ class HomeScreenViewModelTest {
stubInterceptEvents(events = emptyFlow())
stubConfig()
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -1328,7 +1339,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = favoriteWidgetBlock.id,
@ -1464,7 +1475,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = favoriteWidgetBlock.id,
@ -1676,7 +1687,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = favoriteWidgetBlock.id,
@ -1872,7 +1883,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -1948,7 +1959,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -2015,7 +2026,7 @@ class HomeScreenViewModelTest {
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -2100,7 +2111,7 @@ class HomeScreenViewModelTest {
)
}
)
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -2187,7 +2198,7 @@ class HomeScreenViewModelTest {
)
}
)
stubOpenObject(givenObjectView)
stubOpenWidgetObject(givenObjectView)
stubSearchByIds(
subscription = widgetBlock.id,
targets = emptyList()
@ -2222,6 +2233,230 @@ class HomeScreenViewModelTest {
}
}
@Test
fun `should not re-fetch data after updating active view locally and then on mw`() = runTest {
val currentWidgetSourceObject = StubObject(
id = "SOURCE OBJECT 1",
links = emptyList(),
objectType = ObjectTypeIds.SET
)
val widgetSourceLink = StubLinkToObjectBlock(
id = "SOURCE LINK",
target = currentWidgetSourceObject.id
)
val widgetBlock = StubWidgetBlock(
id = "WIDGET BLOCK",
layout = Block.Content.Widget.Layout.LIST,
children = listOf(widgetSourceLink.id)
)
val smartWidgetBlock = StubSmartBlock(
id = WIDGET_OBJECT_ID,
children = listOf(widgetBlock.id),
)
val givenWidgetObjectView = StubObjectView(
root = WIDGET_OBJECT_ID,
blocks = listOf(
smartWidgetBlock,
widgetBlock,
widgetSourceLink
),
details = mapOf(
currentWidgetSourceObject.id to currentWidgetSourceObject.map
)
)
val dataViewFirstView = StubDataViewView()
val dataViewSecondView = StubDataViewView(
filters = listOf(StubFilter())
)
val dataViewBlock = StubDataView(
views = listOf(dataViewFirstView, dataViewSecondView)
)
val dataViewSmartBlock = StubSmartBlock(
id = currentWidgetSourceObject.id,
children = listOf(dataViewBlock.id)
)
val givenDataViewObjectView = StubObjectView(
root = dataViewSmartBlock.id,
blocks = listOf(
dataViewSmartBlock,
dataViewBlock
),
details = mapOf(
currentWidgetSourceObject.id to mapOf(
Relations.ID to currentWidgetSourceObject.map
)
)
)
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenWidgetObject(givenWidgetObjectView)
stubGetObject(givenDataViewObjectView)
stubWidgetActiveView(widgetBlock)
stubSetWidgetActiveView(
widget = widgetBlock.id,
view = dataViewSecondView.id
)
val firstTimeParams = StoreSearchParams(
subscription = widgetBlock.id,
filters = buildList {
addAll(ObjectSearchConstants.defaultDataViewFilters(config.workspace))
add(
DVFilter(
relation = Relations.TYPE,
condition = DVFilterCondition.NOT_IN,
value = listOf(
ObjectTypeIds.OBJECT_TYPE,
ObjectTypeIds.RELATION,
ObjectTypeIds.TEMPLATE,
ObjectTypeIds.IMAGE,
ObjectTypeIds.FILE,
ObjectTypeIds.VIDEO,
ObjectTypeIds.AUDIO,
ObjectTypeIds.DASHBOARD,
ObjectTypeIds.RELATION_OPTION,
ObjectTypeIds.DASHBOARD,
ObjectTypeIds.DATE
)
),
)
},
sorts = emptyList(),
limit = WidgetConfig.DEFAULT_LIST_MAX_COUNT,
keys = buildList {
addAll(ObjectSearchConstants.defaultDataViewKeys)
add(Relations.DESCRIPTION)
}.distinct(),
source = currentWidgetSourceObject.setOf
)
// Params expected after switching active view
val secondTimeParams = firstTimeParams.copy(
filters = buildList {
addAll(dataViewSecondView.filters)
addAll(firstTimeParams.filters)
}
)
stubDefaultSearch(
params = firstTimeParams,
results = emptyList()
)
stubDefaultSearch(
params = secondTimeParams,
results = emptyList()
)
stubCollapsedWidgetState(any())
stubGetWidgetSession()
stubGetDefaultPageType()
stubObserveSpaceObject()
// Using real implementation here
activeViewStateHolder = WidgetActiveViewStateHolder.Impl()
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.SetOfObjects && firstWidget.tabs.first().isSelected
}
verifyBlocking(getObject, times(1)) {
run(params = currentWidgetSourceObject.id)
}
verify(storelessSubscriptionContainer, times(1)).subscribe(
StoreSearchByIdsParams(
subscription = HomeScreenViewModel.HOME_SCREEN_SPACE_OBJECT_SUBSCRIPTION,
targets = listOf(config.workspace),
keys = listOf(
Relations.ID,
Relations.ICON_EMOJI,
Relations.ICON_IMAGE,
Relations.ICON_OPTION
)
)
)
verify(storelessSubscriptionContainer, times(1)).subscribe(
firstTimeParams
)
advanceUntilIdle()
// Changing active view
vm.onChangeCurrentWidgetView(
widget = widgetBlock.id,
view = dataViewSecondView.id
)
advanceUntilIdle()
val thirdTimeItem = awaitItem()
advanceUntilIdle()
assertTrue {
val firstWidget = thirdTimeItem.first()
firstWidget is WidgetView.SetOfObjects && firstWidget.tabs.last().isSelected
}
verify(storelessSubscriptionContainer, times(1)).subscribe(
secondTimeParams
)
verifyNoMoreInteractions(storelessSubscriptionContainer)
}
}
private fun stubSetWidgetActiveView(
widget: Id,
view: Id,
) {
setWidgetActiveView.stub {
on {
stream(
params = SetWidgetActiveView.Params(
ctx = config.widgets,
widget = widget,
view = view
)
)
} doReturn flowOf(
Resultat.Success(
Payload(
context = WIDGET_OBJECT_ID,
events = listOf(
Event.Command.Widgets.SetWidget(
context = WIDGET_OBJECT_ID,
widget = widget,
activeView = view
)
)
)
)
)
}
}
private fun stubInterceptEvents(events: Flow<List<Event>>) {
interceptEvents.stub {
on { build(InterceptEvents.Params(WIDGET_OBJECT_ID)) } doReturn events
@ -2234,7 +2469,7 @@ class HomeScreenViewModelTest {
}
}
private fun stubOpenObject(givenObjectView: ObjectView) {
private fun stubOpenWidgetObject(givenObjectView: ObjectView) {
openObject.stub {
on {
stream(OpenObject.Params(WIDGET_OBJECT_ID, false))
@ -2246,6 +2481,16 @@ class HomeScreenViewModelTest {
}
}
private fun stubGetObject(
givenObjectView: ObjectView
) {
getObject.stub {
onBlocking {
run(givenObjectView.root)
} doReturn givenObjectView
}
}
private fun stubCloseObject() {
closeObject.stub {
onBlocking {
@ -2324,6 +2569,16 @@ class HomeScreenViewModelTest {
}
}
private fun stubGetDefaultPageType() {
getDefaultPageType.stub {
onBlocking {
execute(any())
} doReturn Resultat.Success(
GetDefaultPageType.Response(null, null)
)
}
}
private fun buildViewModel() = HomeScreenViewModel(
configStorage = configStorage,
interceptEvents = interceptEvents,
@ -2352,7 +2607,8 @@ class HomeScreenViewModelTest {
saveWidgetSession = saveWidgetSession,
spaceGradientProvider = spaceGradientProvider,
storeOfObjectTypes = storeOfObjectTypes,
objectWatcher = objectWatcher
objectWatcher = objectWatcher,
setWidgetActiveView = setWidgetActiveView
)
companion object {