From b833601f81b6f085c33a7af0eb25098e7572abb0 Mon Sep 17 00:00:00 2001 From: Evgenii Kozlov Date: Tue, 23 Apr 2024 13:16:57 +0200 Subject: [PATCH] DROID-2460 Multiplayer | Fix | Fix share limit counter (#1156) --- .../DefaultUserPermissionProvider.kt | 19 +++++ .../SpaceViewSubscriptionContainer.kt | 16 +++- .../SpaceViewSubscriptionContainerTest.kt | 80 ++++++++++++++++--- .../spaces/SpaceSettingsViewModel.kt | 2 +- .../anytype/ui_settings/space/Settings.kt | 19 ++--- 5 files changed, 114 insertions(+), 22 deletions(-) diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/DefaultUserPermissionProvider.kt b/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/DefaultUserPermissionProvider.kt index 4501273ac7..6a1e5403a7 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/DefaultUserPermissionProvider.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/DefaultUserPermissionProvider.kt @@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.multiplayer import com.anytypeio.anytype.core_models.DVFilter import com.anytypeio.anytype.core_models.DVFilterCondition +import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.Relations @@ -43,6 +44,12 @@ interface UserPermissionProvider { * @return [SpaceMemberPermissions] for [space] or null if user permission could not be defined. */ fun observe(space: SpaceId) : Flow + + /** + * Provide permissions of the current user in all available spaces. + * Maps space to permissions. + */ + fun all() : Flow> } class DefaultUserPermissionProvider @Inject constructor( @@ -107,6 +114,18 @@ class DefaultUserPermissionProvider @Inject constructor( } } + override fun all(): Flow> { + return members.map { all -> + all.filter { member -> + !member.spaceId.isNullOrEmpty() + }.associate { member -> + val space = requireNotNull(member.spaceId) + val permissions = member.permissions ?: SpaceMemberPermissions.NO_PERMISSIONS + space to permissions + } + } + } + override fun stop() { clear() scope.launch(dispatchers.io) { diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainer.kt b/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainer.kt index a90c7631ef..670f72ef45 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainer.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainer.kt @@ -4,10 +4,14 @@ import com.anytypeio.anytype.core_models.DVFilter import com.anytypeio.anytype.core_models.DVFilterCondition import com.anytypeio.anytype.core_models.DVSort import com.anytypeio.anytype.core_models.DVSortType +import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType +import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType.SHARED +import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions +import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions.OWNER import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_models.restrictions.SpaceStatus import com.anytypeio.anytype.domain.account.AwaitAccountStartManager @@ -139,10 +143,16 @@ interface SpaceViewSubscriptionContainer { } } -fun SpaceViewSubscriptionContainer.isSharingLimitReached() : Flow { - val sharedSpacesCount = observe().map { spaceViews -> +fun SpaceViewSubscriptionContainer.isSharingLimitReached( + spaceToUserPermissions: Flow> +) : Flow { + val sharedSpacesCount = combine( + observe(), + spaceToUserPermissions + ) { spaceViews, permissions -> spaceViews.count { spaceView -> - spaceView.spaceAccessType == SpaceAccessType.SHARED + val permission = permissions[spaceView.targetSpaceId] + spaceView.spaceAccessType == SHARED && permission == OWNER } } val sharedSpaceLimit = observe().map { spaceViews -> diff --git a/domain/src/test/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainerTest.kt b/domain/src/test/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainerTest.kt index 1ee945542c..615fb8f86f 100644 --- a/domain/src/test/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainerTest.kt +++ b/domain/src/test/java/com/anytypeio/anytype/domain/multiplayer/SpaceViewSubscriptionContainerTest.kt @@ -3,6 +3,7 @@ package com.anytypeio.anytype.domain.multiplayer import app.cash.turbine.test import com.anytypeio.anytype.core_models.StubSpaceView import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType +import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions import kotlin.test.assertEquals import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest @@ -37,6 +38,11 @@ class SpaceViewSubscriptionContainerTest { spaceAccessType = SpaceAccessType.PRIVATE ) + val permissions = mapOf( + defaultSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER, + privateSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER + ) + container.stub { on { observe() @@ -45,7 +51,7 @@ class SpaceViewSubscriptionContainerTest { ) } - container.isSharingLimitReached().test { + container.isSharingLimitReached(flowOf(permissions)).test { val result = awaitItem() assertEquals( expected = true, @@ -68,6 +74,11 @@ class SpaceViewSubscriptionContainerTest { spaceAccessType = SpaceAccessType.PRIVATE ) + val permissions = mapOf( + defaultSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER, + privateSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER + ) + container.stub { on { observe() @@ -76,7 +87,7 @@ class SpaceViewSubscriptionContainerTest { ) } - container.isSharingLimitReached().test { + container.isSharingLimitReached(flowOf(permissions)).test { val result = awaitItem() assertEquals( expected = false, @@ -87,27 +98,32 @@ class SpaceViewSubscriptionContainerTest { } @Test - fun `should reach limit if limit is 1 and there is one shared space already`() = runTest { + fun `should reach limit if limit is 1 and there is one shared space already where user is owner`() = runTest { val defaultSpaceView = StubSpaceView( sharedSpaceLimit = 1, spaceAccessType = SpaceAccessType.DEFAULT ) - val privateSpaceView = StubSpaceView( + val sharedSpaceView = StubSpaceView( sharedSpaceLimit = 0, spaceAccessType = SpaceAccessType.SHARED ) + val permissions = mapOf( + defaultSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER, + sharedSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER + ) + container.stub { on { observe() } doReturn flowOf( - listOf(defaultSpaceView, privateSpaceView) + listOf(defaultSpaceView, sharedSpaceView) ) } - container.isSharingLimitReached().test { + container.isSharingLimitReached(flowOf(permissions)).test { val result = awaitItem() assertEquals( expected = true, @@ -118,7 +134,43 @@ class SpaceViewSubscriptionContainerTest { } @Test - fun `should not reach limit if limit is 2 and there is one shared space already`() = runTest { + fun `should not reach limit if limit is 1 and there is one shared space already where user is owner`() = runTest { + + val defaultSpaceView = StubSpaceView( + sharedSpaceLimit = 1, + spaceAccessType = SpaceAccessType.DEFAULT + ) + + val sharedSpaceView = StubSpaceView( + sharedSpaceLimit = 0, + spaceAccessType = SpaceAccessType.SHARED + ) + + val permissions = mapOf( + defaultSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER, + sharedSpaceView.targetSpaceId!! to SpaceMemberPermissions.READER + ) + + container.stub { + on { + observe() + } doReturn flowOf( + listOf(defaultSpaceView, sharedSpaceView) + ) + } + + container.isSharingLimitReached(flowOf(permissions)).test { + val result = awaitItem() + assertEquals( + expected = false, + actual = result + ) + awaitComplete() + } + } + + @Test + fun `should not reach limit if limit is 2 and there is one shared space already where user is owner`() = runTest { val defaultSpaceView = StubSpaceView( sharedSpaceLimit = 2, @@ -130,6 +182,11 @@ class SpaceViewSubscriptionContainerTest { spaceAccessType = SpaceAccessType.SHARED ) + val permissions = mapOf( + defaultSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER, + privateSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER + ) + container.stub { on { observe() @@ -138,7 +195,7 @@ class SpaceViewSubscriptionContainerTest { ) } - container.isSharingLimitReached().test { + container.isSharingLimitReached(flowOf(permissions)).test { val result = awaitItem() assertEquals( expected = false, @@ -161,6 +218,11 @@ class SpaceViewSubscriptionContainerTest { spaceAccessType = SpaceAccessType.PRIVATE ) + val permissions = mapOf( + defaultSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER, + privateSpaceView.targetSpaceId!! to SpaceMemberPermissions.OWNER + ) + container.stub { on { observe() @@ -169,7 +231,7 @@ class SpaceViewSubscriptionContainerTest { ) } - container.isSharingLimitReached().test { + container.isSharingLimitReached(flowOf(permissions)).test { val result = awaitItem() assertEquals( expected = true, diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/SpaceSettingsViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/SpaceSettingsViewModel.kt index 20e198bb27..41d2f2d95d 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/SpaceSettingsViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/SpaceSettingsViewModel.kt @@ -75,7 +75,7 @@ class SpaceSettingsViewModel( combine( spaceViewContainer.observe(params.space), userPermissionProvider.observe(params.space), - spaceViewContainer.isSharingLimitReached() + spaceViewContainer.isSharingLimitReached(userPermissionProvider.all()) ) { spaceView, permission, shareLimitReached -> SpaceData( name = spaceView.name.orEmpty(), diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/Settings.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/Settings.kt index 9379c1e383..0ca427c78e 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/Settings.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/Settings.kt @@ -363,20 +363,21 @@ fun PrivateSpaceSharing( .padding(start = 20.dp) .align(Alignment.CenterStart), text = stringResource(id = R.string.space_type_private_space), - color = colorResource(id = R.color.text_primary), + color = if (shareLimitReached) + colorResource(id = R.color.text_secondary) + else + colorResource(id = R.color.text_primary), style = BodyRegular ) Row( modifier = Modifier.align(Alignment.CenterEnd) ) { - if (!shareLimitReached) { - Text( - modifier = Modifier.align(Alignment.CenterVertically), - text = stringResource(id = R.string.multiplayer_share), - color = colorResource(id = R.color.text_secondary), - style = BodyRegular - ) - } + Text( + modifier = Modifier.align(Alignment.CenterVertically), + text = stringResource(id = R.string.multiplayer_share), + color = colorResource(id = R.color.text_secondary), + style = BodyRegular + ) Spacer(Modifier.width(10.dp)) Image( painter = painterResource(R.drawable.ic_arrow_forward),