From ad1492120ecc5ab29fefec1f73e481e2bda7e9dc Mon Sep 17 00:00:00 2001 From: Evgenii Kozlov Date: Mon, 26 May 2025 22:16:40 +0200 Subject: [PATCH] DROID-3362 Widgets | Fix | Should not trigger container creation on irrelevant keys updates (#2467) --- .../presentation/home/HomeScreenViewModel.kt | 13 ++++- .../anytype/presentation/widgets/Widget.kt | 5 ++ .../home/HomeScreenViewModelTest.kt | 7 ++- .../splash/SplashViewModelTest.kt | 56 ++++++++++++++++++- .../anytypeio/anytype/core_models/DataView.kt | 8 ++- 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt index 24f21b3fc8..9f711e3a26 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModel.kt @@ -1350,7 +1350,18 @@ class HomeScreenViewModel( ) } is Event.Command.Details -> { - curr = curr.copy(details = curr.details.process(e)) + if (e is Event.Command.Details.Amend) { + val hasTargetKeyValueChanges = Widget.Source.SOURCE_KEYS.any { key -> + key in e.details.keys + } + if (hasTargetKeyValueChanges) { + curr = curr.copy(details = curr.details.process(e)) + } else { + Timber.d("Widget source reducer: Ignoring Amend event: no relevant keys in ${e.details.keys}") + } + } else { + curr = curr.copy(details = curr.details.process(e)) + } } is Event.Command.LinkGranularChange -> { curr = curr.copy( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/Widget.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/Widget.kt index e968cd23c2..e056a46ede 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/Widget.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/widgets/Widget.kt @@ -10,6 +10,7 @@ import com.anytypeio.anytype.core_models.SupportedLayouts.isSupportedForWidgets import com.anytypeio.anytype.core_models.ext.asMap import com.anytypeio.anytype.core_models.widgets.BundledWidgetSourceIds import com.anytypeio.anytype.domain.primitives.FieldParser +import com.anytypeio.anytype.presentation.search.ObjectSearchConstants import com.anytypeio.anytype.presentation.widgets.WidgetView.Name sealed class Widget { @@ -122,6 +123,10 @@ sealed class Widget { override val type: Id? = null } } + + companion object { + val SOURCE_KEYS = ObjectSearchConstants.defaultKeys + } } } diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt index 214cd21ab6..93ffaddcc5 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/home/HomeScreenViewModelTest.kt @@ -37,6 +37,7 @@ import com.anytypeio.anytype.domain.base.Resultat import com.anytypeio.anytype.domain.bin.EmptyBin import com.anytypeio.anytype.domain.block.interactor.CreateBlock import com.anytypeio.anytype.domain.block.interactor.Move +import com.anytypeio.anytype.domain.chats.ChatPreviewContainer import com.anytypeio.anytype.domain.collections.AddObjectToCollection import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.domain.config.Gateway @@ -284,6 +285,9 @@ class HomeScreenViewModelTest { @Mock lateinit var setObjectListIsFavorite: SetObjectListIsFavorite + @Mock + lateinit var chacPreviewContainer: ChatPreviewContainer + lateinit var userPermissionProvider: UserPermissionProvider private val objectPayloadDispatcher = Dispatcher.Default() @@ -2930,7 +2934,8 @@ class HomeScreenViewModelTest { spaceMembers = activeSpaceMemberSubscriptionContainer, spaceViewSubscriptionContainer = spaceViewSubscriptionContainer, deleteSpace = deleteSpace, - setAsFavourite = setObjectListIsFavorite + setAsFavourite = setObjectListIsFavorite, + chatPreviews = chacPreviewContainer ) companion object { diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt index 229a9ba318..9121b595a1 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt @@ -6,6 +6,7 @@ import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_models.StubConfig import com.anytypeio.anytype.core_models.StubSpaceView +import com.anytypeio.anytype.core_models.multiplayer.SpaceUxType import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_models.restrictions.SpaceStatus import com.anytypeio.anytype.domain.auth.interactor.CheckAuthorizationStatus @@ -319,7 +320,8 @@ class SplashViewModelTest { targetSpaceId = space, spaceLocalStatus = SpaceStatus.OK, spaceAccountStatus = SpaceStatus.OK, - chatId = chatId + chatId = chatId, + spaceUxType = SpaceUxType.CHAT ) stubCheckAuthStatus(response) @@ -357,6 +359,58 @@ class SplashViewModelTest { } } + @Test + fun `should navigate to vault even if chat is available if given space has data ux type`() = runTest { + // GIVEN + val deeplink = "test-deeplink" + val status = AuthStatus.AUTHORIZED + val response = Either.Right(status) + + val space = defaultSpaceConfig.space + val chatId = "chat-id" + + val spaceView = StubSpaceView( + targetSpaceId = space, + spaceLocalStatus = SpaceStatus.OK, + spaceAccountStatus = SpaceStatus.OK, + chatId = chatId, + spaceUxType = SpaceUxType.DATA + ) + + stubCheckAuthStatus(response) + stubLaunchWallet() + stubLaunchAccount() + stubGetLastOpenedObject() + + getLastOpenedSpace.stub { + onBlocking { async(Unit) } doReturn Resultat.Success(SpaceId(space)) + } + + initViewModel() + + // WHEN + spaceManager.stub { + on { observe() } doReturn flowOf(defaultSpaceConfig) + } + spaceViewSubscriptionContainer.stub { + on { observe(SpaceId(defaultSpaceConfig.space)) } doReturn flowOf(spaceView) + } + + vm.commands.test { + // Act + vm.onDeepLinkLaunch(deeplink) + + val first = awaitItem() + assertEquals( + expected = SplashViewModel.Command.NavigateToWidgets( + space = space, + deeplink = deeplink + ), + actual = first + ) + } + } + private fun stubCheckAuthStatus(response: Either.Right) { checkAuthorizationStatus.stub { onBlocking { invoke(eq(Unit)) } doReturn response 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 4109d42b06..9732093357 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 @@ -1,6 +1,7 @@ package com.anytypeio.anytype.core_models import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType +import com.anytypeio.anytype.core_models.multiplayer.SpaceUxType import com.anytypeio.anytype.core_models.restrictions.SpaceStatus import com.anytypeio.anytype.test_utils.MockDataFactory @@ -104,8 +105,8 @@ fun StubSpaceView( sharedSpaceLimit: Int? = null, spaceAccountStatus: SpaceStatus? = null, spaceLocalStatus: SpaceStatus? = null, - chatId: Id? = null - + chatId: Id? = null, + spaceUxType: SpaceUxType = SpaceUxType.DATA, ) = ObjectWrapper.SpaceView( map = mapOf( Relations.ID to id, @@ -114,6 +115,7 @@ fun StubSpaceView( Relations.SPACE_ACCESS_TYPE to spaceAccessType.code.toDouble(), Relations.SHARED_SPACES_LIMIT to sharedSpaceLimit?.toDouble(), Relations.SPACE_ACCOUNT_STATUS to spaceAccountStatus?.code?.toDouble(), - Relations.SPACE_LOCAL_STATUS to spaceLocalStatus?.code?.toDouble() + Relations.SPACE_LOCAL_STATUS to spaceLocalStatus?.code?.toDouble(), + Relations.SPACE_UX_TYPE to spaceUxType.code.toDouble() ) ) \ No newline at end of file