mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-2378 Membership | Enhancement | Introduce incentives on share-space screen (#1302)
This commit is contained in:
parent
d6d871fe57
commit
bc5102337c
17 changed files with 923 additions and 295 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
|||
!.idea/vcs.xml
|
||||
!.idea/copyright
|
||||
*.iml
|
||||
/app/release
|
||||
.gradle
|
||||
/local.properties
|
||||
/configuration.properties
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.anytypeio.anytype.di.common.ComponentDependencies
|
|||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.multiplayer.SpaceJoinRequestViewModel
|
||||
|
@ -57,4 +58,5 @@ interface SpaceJoinRequestDependencies : ComponentDependencies {
|
|||
fun spaceManager(): SpaceManager
|
||||
fun analytics(): Analytics
|
||||
fun analyticSpaceHelper(): AnalyticSpaceHelperDelegate
|
||||
fun userPermissions(): UserPermissionProvider
|
||||
}
|
|
@ -65,7 +65,9 @@ class ShareSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
onGenerateInviteLinkClicked = vm::onGenerateSpaceInviteLink,
|
||||
onMoreInfoClicked = vm::onMoreInfoClicked,
|
||||
onShareQrCodeClicked = vm::onShareQrCodeClicked,
|
||||
onDeleteLinkClicked = vm::onDeleteLinkClicked
|
||||
onDeleteLinkClicked = vm::onDeleteLinkClicked,
|
||||
incentiveState = vm.showIncentive.collectAsStateWithLifecycle().value,
|
||||
onIncentiveClicked = vm::onIncentiveClicked
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
|
@ -181,6 +183,9 @@ class ShareSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
val msg = getString(R.string.multiplayer_toast_permission_not_allowed)
|
||||
toast(msg)
|
||||
}
|
||||
Command.ShowMembershipScreen -> {
|
||||
findNavController().navigate(R.id.paymentsScreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.core_ui.features.multiplayer
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
@ -37,7 +38,8 @@ import com.anytypeio.anytype.core_ui.views.ButtonSize
|
|||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(backgroundColor = 0x000000, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")
|
||||
fun ShareInviteLinkCardPreview() {
|
||||
ShareInviteLinkCard(
|
||||
link = "https://anytype.io/ibafyrfhfsag6rea3ifffsasssa3ifffsasssga3ifffsasssga3ifffsas",
|
||||
|
@ -49,7 +51,8 @@ fun ShareInviteLinkCardPreview() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(backgroundColor = 0x000000, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")
|
||||
fun GenerateInviteLinkCardPreview() {
|
||||
GenerateInviteLinkCard(
|
||||
modifier = Modifier,
|
||||
|
@ -120,14 +123,7 @@ fun ShareInviteLinkCard(
|
|||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.multiplayer_share_invite_link_description),
|
||||
style = BodyCalloutRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(horizontal = 20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp)
|
||||
|
@ -140,12 +136,19 @@ fun ShareInviteLinkCard(
|
|||
style = BodyCalloutRegular,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
Divider()
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.multiplayer_share_invite_link_description),
|
||||
style = BodyCalloutRegular,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
modifier = Modifier.padding(horizontal = 20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
package com.anytypeio.anytype.core_ui.features.multiplayer
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.AnchoredDraggableState
|
||||
import androidx.compose.foundation.gestures.DraggableAnchors
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.anchoredDraggable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
@ -12,17 +19,22 @@ import androidx.compose.foundation.layout.Spacer
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
@ -31,7 +43,10 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -40,6 +55,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
@ -60,6 +76,8 @@ import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
|||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSecondary
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonUpgrade
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
|
@ -68,12 +86,14 @@ import com.anytypeio.anytype.presentation.multiplayer.ShareSpaceViewModel
|
|||
import com.anytypeio.anytype.presentation.multiplayer.ShareSpaceViewModel.ShareLinkViewState
|
||||
import com.anytypeio.anytype.presentation.objects.SpaceMemberIconView
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ShareSpaceScreen(
|
||||
spaceAccessType: SpaceAccessType?,
|
||||
isCurrentUserOwner: Boolean,
|
||||
members: List<ShareSpaceMemberView>,
|
||||
shareLinkViewState: ShareLinkViewState,
|
||||
incentiveState: ShareSpaceViewModel.ShareSpaceIncentiveState,
|
||||
onGenerateInviteLinkClicked: () -> Unit,
|
||||
onShareInviteLinkClicked: () -> Unit,
|
||||
onViewRequestClicked: (ShareSpaceMemberView) -> Unit,
|
||||
|
@ -84,142 +104,184 @@ fun ShareSpaceScreen(
|
|||
onStopSharingClicked: () -> Unit,
|
||||
onMoreInfoClicked: () -> Unit,
|
||||
onShareQrCodeClicked: () -> Unit,
|
||||
onDeleteLinkClicked: () -> Unit
|
||||
onDeleteLinkClicked: () -> Unit,
|
||||
onIncentiveClicked: () -> Unit
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
Dragger(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.padding(vertical = 6.dp)
|
||||
)
|
||||
}
|
||||
val nestedScrollInteropConnection = rememberNestedScrollInteropConnection()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(nestedScrollInteropConnection)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Dragger(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.padding(vertical = 6.dp)
|
||||
)
|
||||
}
|
||||
item {
|
||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
if (isCurrentUserOwner) {
|
||||
Toolbar(title = stringResource(R.string.multiplayer_sharing))
|
||||
} else {
|
||||
Toolbar(title = stringResource(R.string.multiplayer_members))
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = 16.dp)
|
||||
) {
|
||||
if (isCurrentUserOwner) {
|
||||
Toolbar(title = stringResource(R.string.multiplayer_sharing))
|
||||
} else {
|
||||
Toolbar(title = stringResource(R.string.multiplayer_members))
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_action_more),
|
||||
contentDescription = "Menu button",
|
||||
modifier = Modifier.noRippleClickable {
|
||||
isMenuExpanded = true
|
||||
}
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = 16.dp)
|
||||
DropdownMenu(
|
||||
expanded = isMenuExpanded,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded = false
|
||||
},
|
||||
modifier = Modifier.background(
|
||||
color = colorResource(id = R.color.background_secondary)
|
||||
)
|
||||
) {
|
||||
if (isCurrentUserOwner) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_action_more),
|
||||
contentDescription = "Menu button",
|
||||
modifier = Modifier.noRippleClickable {
|
||||
isMenuExpanded = true
|
||||
}
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onMoreInfoClicked()
|
||||
isMenuExpanded = false
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.multiplayer_more_info),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.weight(1.0f)
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = isMenuExpanded,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded = false
|
||||
},
|
||||
modifier = Modifier.background(
|
||||
color = colorResource(id = R.color.background_secondary)
|
||||
if (spaceAccessType == SpaceAccessType.SHARED) {
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp
|
||||
)
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onMoreInfoClicked()
|
||||
onStopSharingClicked()
|
||||
isMenuExpanded = false
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.multiplayer_more_info),
|
||||
text = stringResource(id = R.string.multiplayer_space_stop_sharing),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
color = colorResource(id = R.color.palette_dark_red),
|
||||
modifier = Modifier.weight(1.0f)
|
||||
)
|
||||
}
|
||||
if (spaceAccessType == SpaceAccessType.SHARED) {
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onStopSharingClicked()
|
||||
isMenuExpanded = false
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.multiplayer_space_stop_sharing),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.palette_dark_red),
|
||||
modifier = Modifier.weight(1.0f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isCurrentUserOwner) {
|
||||
item {
|
||||
Section(
|
||||
title = stringResource(R.string.multiplayer_members_and_requests)
|
||||
)
|
||||
}
|
||||
Section(
|
||||
title = stringResource(R.string.multiplayer_members_and_requests)
|
||||
)
|
||||
}
|
||||
members.forEachIndexed { index, member ->
|
||||
item {
|
||||
when(val config = member.config) {
|
||||
is ShareSpaceMemberView.Config.Member -> {
|
||||
SpaceMember(
|
||||
member = member.obj,
|
||||
isCurrentUserOwner = isCurrentUserOwner,
|
||||
config = config,
|
||||
onCanEditClicked = {
|
||||
onCanEditClicked(member)
|
||||
},
|
||||
onCanViewClicked = {
|
||||
onCanViewClicked(member)
|
||||
},
|
||||
onRemoveMemberClicked = {
|
||||
onRemoveMemberClicked(member)
|
||||
},
|
||||
icon = member.icon,
|
||||
canEditEnabled = member.canEditEnabled,
|
||||
canReadEnabled = member.canReadEnabled,
|
||||
isUser = member.isUser
|
||||
)
|
||||
Incentive(
|
||||
incentiveState = incentiveState,
|
||||
onIncentiveClicked = onIncentiveClicked
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
) {
|
||||
members.forEachIndexed { index, member ->
|
||||
item {
|
||||
when (val config = member.config) {
|
||||
is ShareSpaceMemberView.Config.Member -> {
|
||||
SpaceMember(
|
||||
member = member.obj,
|
||||
isCurrentUserOwner = isCurrentUserOwner,
|
||||
config = config,
|
||||
onCanEditClicked = {
|
||||
onCanEditClicked(member)
|
||||
},
|
||||
onCanViewClicked = {
|
||||
onCanViewClicked(member)
|
||||
},
|
||||
onRemoveMemberClicked = {
|
||||
onRemoveMemberClicked(member)
|
||||
},
|
||||
icon = member.icon,
|
||||
canEditEnabled = member.canEditEnabled,
|
||||
canReadEnabled = member.canReadEnabled,
|
||||
isUser = member.isUser
|
||||
)
|
||||
}
|
||||
|
||||
is ShareSpaceMemberView.Config.Request -> {
|
||||
SpaceMemberRequest(
|
||||
member = member.obj,
|
||||
icon = member.icon,
|
||||
request = config,
|
||||
onViewRequestClicked = {
|
||||
onViewRequestClicked(member)
|
||||
},
|
||||
onApproveLeaveRequestClicked = {
|
||||
onApproveLeaveRequestClicked(member)
|
||||
},
|
||||
isUser = member.isUser
|
||||
)
|
||||
}
|
||||
}
|
||||
is ShareSpaceMemberView.Config.Request -> {
|
||||
SpaceMemberRequest(
|
||||
member = member.obj,
|
||||
icon = member.icon,
|
||||
request = config,
|
||||
onViewRequestClicked = {
|
||||
onViewRequestClicked(member)
|
||||
},
|
||||
onApproveLeaveRequestClicked = {
|
||||
onApproveLeaveRequestClicked(member)
|
||||
},
|
||||
isUser = member.isUser
|
||||
)
|
||||
if (index != members.lastIndex) {
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
if (index != members.lastIndex) {
|
||||
Divider()
|
||||
}
|
||||
if (members.size > 2) {
|
||||
// Workaround adding footer to prevent content invisible behind link card
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(324.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (members.size > 4) {
|
||||
// Workaround adding footer to prevent content invisible behind link card
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(324.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val density = LocalDensity.current
|
||||
val draggedDownAnchorTop = with(density) { 250.dp.toPx() }
|
||||
|
||||
val anchors = DraggableAnchors {
|
||||
DragValue.DRAGGED_DOWN at draggedDownAnchorTop
|
||||
DragValue.DRAGGED_UP at 0f
|
||||
}
|
||||
val anchoredDraggableState = remember {
|
||||
AnchoredDraggableState(
|
||||
initialValue = DragValue.DRAGGED_UP,
|
||||
anchors = anchors,
|
||||
positionalThreshold = { distance: Float -> distance * 0.5f },
|
||||
velocityThreshold = { with(density) { 100.dp.toPx() } },
|
||||
animationSpec = tween()
|
||||
)
|
||||
}
|
||||
val offset =
|
||||
if (anchoredDraggableState.offset.isNaN()) 0 else anchoredDraggableState.offset.toInt()
|
||||
SideEffect {
|
||||
anchoredDraggableState.updateAnchors(anchors)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = shareLinkViewState is ShareLinkViewState.Shared,
|
||||
|
@ -227,7 +289,13 @@ fun ShareSpaceScreen(
|
|||
exit = slideOutVertically { it },
|
||||
modifier = Modifier.align(Alignment.BottomStart)
|
||||
) {
|
||||
Box(modifier = Modifier.padding(16.dp)) {
|
||||
Box(modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.offset {
|
||||
IntOffset(x = 0, y = offset)
|
||||
}
|
||||
.anchoredDraggable(anchoredDraggableState, Orientation.Vertical)
|
||||
) {
|
||||
if (shareLinkViewState is ShareLinkViewState.Shared) {
|
||||
ShareInviteLinkCard(
|
||||
link = shareLinkViewState.link,
|
||||
|
@ -253,6 +321,57 @@ fun ShareSpaceScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Incentive(
|
||||
incentiveState: ShareSpaceViewModel.ShareSpaceIncentiveState,
|
||||
onIncentiveClicked: () -> Unit
|
||||
) {
|
||||
when (incentiveState) {
|
||||
is ShareSpaceViewModel.ShareSpaceIncentiveState.VisibleSpaceReaders -> {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp),
|
||||
text = stringResource(id = R.string.multiplayer_cant_add_members),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
ButtonUpgrade(
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp, bottom = 24.dp, start = 16.dp, end = 16.dp)
|
||||
.height(36.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
onClick = onIncentiveClicked,
|
||||
text = stringResource(id = R.string.multiplayer_upgrade_button)
|
||||
)
|
||||
}
|
||||
|
||||
ShareSpaceViewModel.ShareSpaceIncentiveState.VisibleSpaceEditors -> {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
text = stringResource(id = R.string.multiplayer_cant_add_members),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
ButtonUpgrade(
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp, bottom = 24.dp, start = 16.dp, end = 16.dp)
|
||||
.height(36.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
onClick = onIncentiveClicked,
|
||||
text = stringResource(id = R.string.multiplayer_upgrade_button)
|
||||
)
|
||||
}
|
||||
|
||||
ShareSpaceViewModel.ShareSpaceIncentiveState.Hidden -> {
|
||||
//show nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class DragValue { DRAGGED_DOWN, DRAGGED_UP }
|
||||
|
||||
@Composable
|
||||
private fun SpaceMember(
|
||||
isUser: Boolean,
|
||||
|
@ -304,16 +423,19 @@ private fun SpaceMember(
|
|||
}
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = when(config) {
|
||||
text = when (config) {
|
||||
ShareSpaceMemberView.Config.Member.Writer -> {
|
||||
stringResource(id = R.string.multiplayer_can_edit)
|
||||
}
|
||||
|
||||
ShareSpaceMemberView.Config.Member.Owner -> {
|
||||
stringResource(id = R.string.multiplayer_owner)
|
||||
}
|
||||
|
||||
ShareSpaceMemberView.Config.Member.Reader -> {
|
||||
stringResource(id = R.string.multiplayer_can_view)
|
||||
}
|
||||
|
||||
else -> EMPTY_STRING_VALUE
|
||||
},
|
||||
style = Relations3,
|
||||
|
@ -350,13 +472,13 @@ private fun SpaceMember(
|
|||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.weight(1.0f)
|
||||
)
|
||||
if (config is ShareSpaceMemberView.Config.Member.Reader) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_dropdown_menu_check),
|
||||
contentDescription = "Checked icon",
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
if (config is ShareSpaceMemberView.Config.Member.Reader) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_dropdown_menu_check),
|
||||
contentDescription = "Checked icon",
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
DropdownMenuItem(
|
||||
|
@ -373,13 +495,13 @@ private fun SpaceMember(
|
|||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.weight(1.0f)
|
||||
)
|
||||
if (config is ShareSpaceMemberView.Config.Member.Writer) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_dropdown_menu_check),
|
||||
contentDescription = "Checked icon",
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
if (config is ShareSpaceMemberView.Config.Member.Writer) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_dropdown_menu_check),
|
||||
contentDescription = "Checked icon",
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
DropdownMenuItem(
|
||||
|
@ -488,14 +610,15 @@ private fun SpaceMemberRequest(
|
|||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
val color = when(request) {
|
||||
val color = when (request) {
|
||||
ShareSpaceMemberView.Config.Request.Join -> ThemeColor.PINK
|
||||
ShareSpaceMemberView.Config.Request.Leave -> ThemeColor.RED
|
||||
}
|
||||
val text = when(request) {
|
||||
val text = when (request) {
|
||||
ShareSpaceMemberView.Config.Request.Join -> stringResource(
|
||||
id = R.string.multiplayer_join_request
|
||||
)
|
||||
|
||||
ShareSpaceMemberView.Config.Request.Leave -> stringResource(
|
||||
id = R.string.multiplayer_leave_request
|
||||
)
|
||||
|
@ -515,7 +638,7 @@ private fun SpaceMemberRequest(
|
|||
style = Relations1
|
||||
)
|
||||
}
|
||||
when(request) {
|
||||
when (request) {
|
||||
ShareSpaceMemberView.Config.Request.Join -> {
|
||||
ButtonSecondary(
|
||||
text = stringResource(R.string.multiplayer_view_request),
|
||||
|
@ -526,6 +649,7 @@ private fun SpaceMemberRequest(
|
|||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
|
||||
ShareSpaceMemberView.Config.Request.Leave -> {
|
||||
ButtonSecondary(
|
||||
text = stringResource(R.string.multiplayer_approve_request),
|
||||
|
@ -580,7 +704,18 @@ fun SpaceLeaveRequestPreview() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(
|
||||
backgroundColor = 0xFFFFFFFF,
|
||||
showBackground = true,
|
||||
uiMode = Configuration.UI_MODE_NIGHT_NO,
|
||||
name = "Light Mode"
|
||||
)
|
||||
@Preview(
|
||||
backgroundColor = 0x000000,
|
||||
showBackground = true,
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
name = "Dark Mode"
|
||||
)
|
||||
fun ShareSpaceScreenPreview() {
|
||||
ShareSpaceScreen(
|
||||
shareLinkViewState = ShareSpaceViewModel.ShareLinkViewState.Shared(
|
||||
|
@ -649,13 +784,15 @@ fun ShareSpaceScreenPreview() {
|
|||
onRemoveMemberClicked = {},
|
||||
onCanViewClicked = {},
|
||||
onCanEditClicked = {},
|
||||
isCurrentUserOwner = false,
|
||||
isCurrentUserOwner = true,
|
||||
onStopSharingClicked = {},
|
||||
onGenerateInviteLinkClicked = {},
|
||||
onMoreInfoClicked = {},
|
||||
onShareQrCodeClicked = {},
|
||||
onDeleteLinkClicked = {},
|
||||
spaceAccessType = null
|
||||
spaceAccessType = null,
|
||||
incentiveState = ShareSpaceViewModel.ShareSpaceIncentiveState.VisibleSpaceReaders,
|
||||
onIncentiveClicked = {}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.core_ui.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
|
@ -129,9 +130,8 @@ fun ButtonPrimary(
|
|||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed = interactionSource.collectIsPressedAsState()
|
||||
val backgroundColor =
|
||||
if (isPressed.value) colorResource(id = R.color.button_pressed) else colorResource(
|
||||
id = R.color.glyph_selected
|
||||
)
|
||||
if (isPressed.value) colorResource(id = R.color.glyph_button).copy(alpha = 0.15f)
|
||||
else colorResource(id = R.color.glyph_button)
|
||||
|
||||
CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {
|
||||
Button(
|
||||
|
@ -142,8 +142,8 @@ fun ButtonPrimary(
|
|||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = backgroundColor,
|
||||
contentColor = colorResource(id = R.color.button_text),
|
||||
disabledBackgroundColor = colorResource(id = R.color.shape_tertiary),
|
||||
disabledContentColor = colorResource(id = R.color.text_tertiary)
|
||||
disabledBackgroundColor = colorResource(id = R.color.shape_secondary),
|
||||
disabledContentColor = colorResource(id = R.color.text_label_inversion)
|
||||
),
|
||||
modifier = modifier
|
||||
.defaultMinSize(minWidth = 1.dp, minHeight = 1.dp),
|
||||
|
@ -217,9 +217,8 @@ fun ButtonPrimaryLoading(
|
|||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed = interactionSource.collectIsPressedAsState()
|
||||
val backgroundColor =
|
||||
if (isPressed.value) colorResource(id = R.color.button_pressed) else colorResource(
|
||||
id = R.color.glyph_selected
|
||||
)
|
||||
if (isPressed.value) colorResource(id = R.color.glyph_button).copy(alpha = 0.15f)
|
||||
else colorResource(id = R.color.glyph_button)
|
||||
|
||||
CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {
|
||||
Box(modifier = modifierBox, contentAlignment = Alignment.Center) {
|
||||
|
@ -231,8 +230,8 @@ fun ButtonPrimaryLoading(
|
|||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = backgroundColor,
|
||||
contentColor = colorResource(id = R.color.button_text),
|
||||
disabledBackgroundColor = colorResource(id = R.color.shape_tertiary),
|
||||
disabledContentColor = colorResource(id = R.color.text_tertiary)
|
||||
disabledBackgroundColor = colorResource(id = R.color.shape_secondary),
|
||||
disabledContentColor = colorResource(id = R.color.text_label_inversion)
|
||||
),
|
||||
modifier = modifierButton
|
||||
.defaultMinSize(minWidth = 1.dp, minHeight = 1.dp),
|
||||
|
@ -568,7 +567,8 @@ object NoRippleTheme : RippleTheme {
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")
|
||||
fun MyPrimaryButton() {
|
||||
ButtonPrimary(
|
||||
onClick = {},
|
||||
|
@ -581,7 +581,8 @@ fun MyPrimaryButton() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")
|
||||
fun MyPrimaryButtonDisabled() {
|
||||
ButtonPrimary(
|
||||
onClick = {},
|
||||
|
@ -595,7 +596,8 @@ fun MyPrimaryButtonDisabled() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")
|
||||
fun MyPrimaryButtonDark() {
|
||||
ButtonPrimaryDarkTheme(
|
||||
onClick = {},
|
||||
|
@ -608,7 +610,8 @@ fun MyPrimaryButtonDark() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(backgroundColor = 0x000000, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")
|
||||
fun MySecondaryButton() {
|
||||
ButtonSecondary(
|
||||
onClick = {},
|
||||
|
@ -621,7 +624,8 @@ fun MySecondaryButton() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(backgroundColor = 0x000000, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")
|
||||
fun MyWarningButton() {
|
||||
ButtonWarning(
|
||||
onClick = {},
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package com.anytypeio.anytype.core_ui.views
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.ripple.LocalRippleTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
|
||||
@Composable
|
||||
fun ButtonUpgrade(
|
||||
text: String = "",
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
style: TextStyle = BodyCalloutRegular
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed = interactionSource.collectIsPressedAsState()
|
||||
val backgroundColor =
|
||||
if (isPressed.value) colorResource(id = R.color.button_pressed) else colorResource(
|
||||
id = R.color.glyph_selected
|
||||
)
|
||||
|
||||
CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = backgroundColor, shape = RoundedCornerShape(18.dp))
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
indication = null
|
||||
)
|
||||
,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
text = text,
|
||||
style = style,
|
||||
color = colorResource(id = R.color.button_text)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Mode")
|
||||
@Preview(backgroundColor = 0x000000, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Mode")fun MyButtonUpgrade() {
|
||||
ButtonUpgrade(
|
||||
onClick = {},
|
||||
text = "✦ Upgrade",
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
.height(36.dp)
|
||||
)
|
||||
}
|
|
@ -2,19 +2,33 @@ package com.anytypeio.anytype.domain.`object`
|
|||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper.SpaceView
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper.SpaceMember
|
||||
import com.anytypeio.anytype.core_models.multiplayer.ParticipantStatus
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
|
||||
fun SpaceView.canAddWriters(participants: List<SpaceMember>): Boolean {
|
||||
if (!canAddReaders(participants)) return false
|
||||
this.writersLimit?.let {
|
||||
return it > activeWriters(participants)
|
||||
} ?: return true
|
||||
fun SpaceView.canAddWriters(
|
||||
isCurrentUserOwner: Boolean,
|
||||
participants: List<SpaceMember>
|
||||
): Boolean {
|
||||
if (!canAddReaders(isCurrentUserOwner, participants)) return false
|
||||
if (!isCurrentUserOwner) return false
|
||||
if (spaceAccessType != SpaceAccessType.SHARED) return false
|
||||
if (participants.none { it.status == ParticipantStatus.JOINING }) return false
|
||||
val isWritersLimitReached =
|
||||
isSubscriberLimitReached(activeWriters(participants), writersLimit?.toInt())
|
||||
return !isWritersLimitReached
|
||||
}
|
||||
|
||||
fun SpaceView.canAddReaders(participants: List<SpaceMember>): Boolean {
|
||||
this.readersLimit?.let {
|
||||
return it > activeReaders(participants)
|
||||
} ?: return true
|
||||
fun SpaceView.canAddReaders(
|
||||
isCurrentUserOwner: Boolean,
|
||||
participants: List<SpaceMember>
|
||||
): Boolean {
|
||||
if (!isCurrentUserOwner) return false
|
||||
if (spaceAccessType != SpaceAccessType.SHARED) return false
|
||||
if (participants.none { it.status == ParticipantStatus.JOINING }) return false
|
||||
val isReadersLimitReached =
|
||||
isSubscriberLimitReached(activeReaders(participants), readersLimit?.toInt())
|
||||
return !isReadersLimitReached
|
||||
}
|
||||
|
||||
fun SpaceView.canChangeWriterToReader(participants: List<SpaceMember>): Boolean {
|
||||
|
@ -22,9 +36,7 @@ fun SpaceView.canChangeWriterToReader(participants: List<SpaceMember>): Boolean
|
|||
}
|
||||
|
||||
fun SpaceView.canChangeReaderToWriter(participants: List<SpaceMember>): Boolean {
|
||||
writersLimit?.let {
|
||||
return it.toInt() > activeWriters(participants)
|
||||
} ?: return true
|
||||
return !isSubscriberLimitReached(activeWriters(participants), writersLimit?.toInt())
|
||||
}
|
||||
|
||||
private fun activeReaders(participants: List<SpaceMember>): Int =
|
||||
|
@ -43,3 +55,7 @@ private fun activeWriters(participants: List<SpaceMember>): Int =
|
|||
SpaceMemberPermissions.OWNER
|
||||
)
|
||||
}
|
||||
|
||||
private fun isSubscriberLimitReached(currentSubscribers: Int, subscriberLimit: Int?): Boolean {
|
||||
return subscriberLimit?.let { currentSubscribers >= it } ?: false
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.`object`
|
|||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.multiplayer.ParticipantStatus
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
|
@ -12,6 +13,8 @@ class SpaceViewExtKtTest {
|
|||
|
||||
val spaceId = RandomString.make()
|
||||
|
||||
val isCurrentUserOwner = true
|
||||
|
||||
@Test
|
||||
fun `1 participant, zero limits`() {
|
||||
val spaceView = createSpaceView(
|
||||
|
@ -19,220 +22,517 @@ class SpaceViewExtKtTest {
|
|||
readersLimit = 0
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(spaceView.canAddWriters(participants))
|
||||
assertFalse(spaceView.canAddReaders(participants))
|
||||
assertFalse(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertFalse(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertFalse(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1 participant, o`() {
|
||||
fun `1 participant, owner + join`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 3
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(spaceView.canAddWriters(participants))
|
||||
assertTrue(spaceView.canAddReaders(participants))
|
||||
assertTrue(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertTrue(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `2 participants, o w`() {
|
||||
fun `2 participants, owner writer join`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 3
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.WRITER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(spaceView.canAddWriters(participants))
|
||||
assertTrue(spaceView.canAddReaders(participants))
|
||||
assertTrue(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertTrue(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `2 participants, o r`() {
|
||||
fun `2 participants, owner reader join`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 3
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.READER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(spaceView.canAddWriters(participants))
|
||||
assertTrue(spaceView.canAddReaders(participants))
|
||||
assertTrue(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertTrue(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `3 participants, o r r`() {
|
||||
fun `3 participants, owner reader reader join`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 3
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.READER,
|
||||
SpaceMemberPermissions.READER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(spaceView.canAddWriters(participants))
|
||||
assertFalse(spaceView.canAddReaders(participants))
|
||||
assertFalse(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertFalse(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertTrue(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `3 participants, o w r`() {
|
||||
fun `3 participants, owner writer reader join`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 3
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.READER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(spaceView.canAddWriters(participants))
|
||||
assertFalse(spaceView.canAddReaders(participants))
|
||||
assertFalse(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertFalse(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertTrue(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `3 participants, o w w`() {
|
||||
fun `3 participants, owner writer writer join`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 3
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.WRITER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(spaceView.canAddWriters(participants))
|
||||
assertFalse(spaceView.canAddReaders(participants))
|
||||
assertFalse(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertFalse(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertFalse(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `3 participants, o w w, diff limits`() {
|
||||
fun `3 participants, owner writer writer join, diff limits`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 5
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.WRITER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(spaceView.canAddWriters(participants))
|
||||
assertTrue(spaceView.canAddReaders(participants))
|
||||
assertFalse(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertFalse(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `3 participants, o w r, diff limits`() {
|
||||
fun `3 participants, owner writer reader join, diff limits`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 5
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.READER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(spaceView.canAddWriters(participants))
|
||||
assertTrue(spaceView.canAddReaders(participants))
|
||||
assertTrue(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertTrue(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `3 participants, o r r, diff limits`() {
|
||||
fun `3 participants, owner reader reader join, diff limits`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 5
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.READER,
|
||||
SpaceMemberPermissions.READER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(spaceView.canAddWriters(participants))
|
||||
assertTrue(spaceView.canAddReaders(participants))
|
||||
assertTrue(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertTrue(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `4 participants, o w w r, diff limits`() {
|
||||
fun `4 participants, owner writer writer reader join, diff limits`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 5
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.READER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(spaceView.canAddWriters(participants))
|
||||
assertTrue(spaceView.canAddReaders(participants))
|
||||
assertFalse(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertFalse(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `5 participants, o w w r r, diff limits`() {
|
||||
fun `5 participants, o w w r r join, diff limits`() {
|
||||
val spaceView = createSpaceView(
|
||||
writersLimit = 3,
|
||||
readersLimit = 5
|
||||
)
|
||||
|
||||
val participants = createParticipants(
|
||||
SpaceMemberPermissions.OWNER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.WRITER,
|
||||
SpaceMemberPermissions.READER,
|
||||
SpaceMemberPermissions.READER
|
||||
val participants = listOf(
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.OWNER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.WRITER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to SpaceMemberPermissions.READER.code.toDouble(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.ACTIVE.code.toDouble()
|
||||
)
|
||||
),
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.PARTICIPANT_STATUS to ParticipantStatus.JOINING.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(spaceView.canAddWriters(participants))
|
||||
assertFalse(spaceView.canAddReaders(participants))
|
||||
assertFalse(spaceView.canAddWriters(isCurrentUserOwner, participants))
|
||||
assertFalse(spaceView.canAddReaders(isCurrentUserOwner, participants))
|
||||
assertTrue(spaceView.canChangeWriterToReader(participants))
|
||||
assertFalse(spaceView.canChangeReaderToWriter(participants))
|
||||
}
|
||||
|
@ -242,19 +542,10 @@ class SpaceViewExtKtTest {
|
|||
map = mapOf(
|
||||
Relations.ID to spaceId,
|
||||
Relations.WRITERS_LIMIT to writersLimit.toDouble(),
|
||||
Relations.READERS_LIMIT to readersLimit.toDouble()
|
||||
Relations.READERS_LIMIT to readersLimit.toDouble(),
|
||||
Relations.SPACE_ACCESS_TYPE to 2.0
|
||||
)
|
||||
)
|
||||
|
||||
private fun createParticipants(vararg permissions: SpaceMemberPermissions) = permissions.map {
|
||||
ObjectWrapper.SpaceMember(
|
||||
map = mapOf(
|
||||
Relations.ID to RandomString.make(),
|
||||
Relations.SPACE_ID to spaceId,
|
||||
Relations.PARTICIPANT_PERMISSIONS to it.code.toDouble()
|
||||
)
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@ kotlinVersion = '1.9.22'
|
|||
|
||||
androidxCoreVersion = "1.13.0"
|
||||
|
||||
androidxComposeVersion = '1.6.6'
|
||||
androidxComposeVersion = '1.6.8'
|
||||
composeKotlinCompilerVersion = '1.5.10'
|
||||
composeMaterial3Version = '1.2.1'
|
||||
composeMaterialVersion = '1.6.6'
|
||||
|
@ -17,7 +17,7 @@ composeReorderableVersion = 'e9ef693f63'
|
|||
accompanistVersion = "0.34.0"
|
||||
appcompatVersion = '1.6.1'
|
||||
androidXAnnotationVersion = '1.7.1'
|
||||
fragmentVersion = "1.6.2"
|
||||
fragmentVersion = "1.8.0"
|
||||
exoplayerVersion = "2.19.1"
|
||||
wireVersion = "4.9.8"
|
||||
glideVersion = "4.14.2"
|
||||
|
|
|
@ -1312,7 +1312,7 @@
|
|||
<!--region MULTIPLAYER -->
|
||||
|
||||
<string name="multiplayer_share_invite_link">Share invite link</string>
|
||||
<string name="multiplayer_share_invite_link_description">Send this link to invite others. Assign access rights upon their request approval</string>
|
||||
<string name="multiplayer_share_invite_link_description">Share this invite link so that others can join your Space. Once they click your link and request access, you can set their access rights.</string>
|
||||
<string name="multiplayer_invite_link">Invite link</string>
|
||||
<string name="multiplayer_join_a_space">Join a space</string>
|
||||
<string name="multiplayer_private_comment_for_a_space_owner">Private comment for a space owner</string>
|
||||
|
@ -1322,6 +1322,8 @@
|
|||
<string name="multiplayer_tap_to_write_request_to_join_comment">Tap to write your comment</string>
|
||||
<string name="multiplayer_share_space">Share space</string>
|
||||
<string name="multiplayer_members_and_requests">Members and requests</string>
|
||||
<string name="multiplayer_cant_add_members">You can’t add more members</string>
|
||||
<string name="multiplayer_upgrade_button">✦ Upgrade</string>
|
||||
<string name="multiplayer_members">Members</string>
|
||||
<string name="multiplayer_leave_request">Leave request</string>
|
||||
<string name="multiplayer_join_request">Join request</string>
|
||||
|
|
|
@ -6,6 +6,7 @@ object MembershipConstants {
|
|||
const val EXPLORER_ID = 1
|
||||
const val BUILDER_ID = 4
|
||||
const val CO_CREATOR_ID = 5
|
||||
const val ANY_TEAM_ID = 7
|
||||
|
||||
const val MEMBERSHIP_LEVEL_DETAILS = "https://anytype.io/pricing"
|
||||
const val PRIVACY_POLICY = "https://anytype.io/app_privacy"
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package com.anytypeio.anytype.presentation.mapper
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.canChangeReaderToWriter
|
||||
import com.anytypeio.anytype.domain.`object`.canChangeWriterToReader
|
||||
import com.anytypeio.anytype.presentation.multiplayer.ShareSpaceMemberView
|
||||
|
||||
fun List<ObjectWrapper.SpaceMember>.toView(
|
||||
spaceView: ObjectWrapper.SpaceView?,
|
||||
urlBuilder: UrlBuilder,
|
||||
isCurrentUserOwner: Boolean,
|
||||
account: Id?
|
||||
): List<ShareSpaceMemberView> {
|
||||
return this.mapNotNull { spaceMember ->
|
||||
if (spaceView == null) return@mapNotNull null
|
||||
val canChangeReaderToWriter = spaceView.canChangeReaderToWriter(participants = this)
|
||||
val canChangeWriterToReader = spaceView.canChangeWriterToReader(participants = this)
|
||||
ShareSpaceMemberView.fromObject(
|
||||
obj = spaceMember,
|
||||
urlBuilder = urlBuilder,
|
||||
canChangeWriterToReader = canChangeWriterToReader,
|
||||
canChangeReaderToWriter = canChangeReaderToWriter,
|
||||
includeRequests = isCurrentUserOwner,
|
||||
account = account
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.anytypeio.anytype.presentation.multiplayer
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.domain.`object`.canAddReaders
|
||||
import com.anytypeio.anytype.domain.`object`.canAddWriters
|
||||
|
||||
fun ObjectWrapper.SpaceView.getIncentiveState(
|
||||
isCurrentUserOwner: Boolean, spaceMembers: List<ObjectWrapper.SpaceMember>
|
||||
): ShareSpaceViewModel.ShareSpaceIncentiveState {
|
||||
val canAddReaders = canAddReaders(isCurrentUserOwner, spaceMembers)
|
||||
val canAddWriters = canAddWriters(isCurrentUserOwner, spaceMembers)
|
||||
return when {
|
||||
!canAddReaders -> ShareSpaceViewModel.ShareSpaceIncentiveState.VisibleSpaceReaders
|
||||
!canAddWriters -> ShareSpaceViewModel.ShareSpaceIncentiveState.VisibleSpaceEditors
|
||||
else -> ShareSpaceViewModel.ShareSpaceIncentiveState.Hidden
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary.SharingSpacesTypes.
|
|||
import com.anytypeio.anytype.analytics.base.EventsDictionary.SharingSpacesTypes.shareTypeQR
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.SharingSpacesTypes.shareTypeRevoke
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.SharingSpacesTypes.shareTypeShareLink
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.SharingSpacesTypes.shareTypeShareQr
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.clickSettingsSpaceShare
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.removeSpaceMember
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.screenRevokeShareLink
|
||||
|
@ -45,7 +44,10 @@ import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
|||
import com.anytypeio.anytype.domain.`object`.canChangeReaderToWriter
|
||||
import com.anytypeio.anytype.domain.`object`.canChangeWriterToReader
|
||||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import com.anytypeio.anytype.presentation.mapper.toView
|
||||
import com.anytypeio.anytype.presentation.objects.SpaceMemberIconView
|
||||
import com.anytypeio.anytype.presentation.objects.toSpaceMembers
|
||||
import com.anytypeio.anytype.presentation.objects.toSpaceView
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.getSpaceMembersSearchParams
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.getSpaceViewSearchParams
|
||||
import javax.inject.Inject
|
||||
|
@ -78,9 +80,7 @@ class ShareSpaceViewModel(
|
|||
val commands = MutableSharedFlow<Command>()
|
||||
val isCurrentUserOwner = MutableStateFlow(false)
|
||||
val spaceAccessType = MutableStateFlow<SpaceAccessType?>(null)
|
||||
|
||||
private var canChangeWriterToReader = false
|
||||
private var canChangeReaderToWriter = false
|
||||
val showIncentive = MutableStateFlow<ShareSpaceIncentiveState>(ShareSpaceIncentiveState.Hidden)
|
||||
|
||||
init {
|
||||
Timber.d("Share-space init with params: $params")
|
||||
|
@ -119,34 +119,27 @@ class ShareSpaceViewModel(
|
|||
container.subscribe(spaceMembersSearchParams),
|
||||
isCurrentUserOwner
|
||||
) { spaceResponse, membersResponse, isCurrentUserOwner ->
|
||||
|
||||
val spaceView = spaceResponse.firstOrNull()?.let { ObjectWrapper.SpaceView(it.map) }
|
||||
val spaceMembers = membersResponse.map { ObjectWrapper.SpaceMember(it.map) }
|
||||
|
||||
canChangeReaderToWriter = spaceView?.canChangeReaderToWriter(spaceMembers) ?: false
|
||||
canChangeWriterToReader = spaceView?.canChangeWriterToReader(spaceMembers) ?: false
|
||||
|
||||
val spaceViewMembers = spaceMembers.mapNotNull { m ->
|
||||
ShareSpaceMemberView.fromObject(
|
||||
obj = m,
|
||||
urlBuilder = urlBuilder,
|
||||
canChangeWriterToReader = canChangeWriterToReader,
|
||||
canChangeReaderToWriter = canChangeReaderToWriter,
|
||||
includeRequests = isCurrentUserOwner,
|
||||
account = account
|
||||
)
|
||||
}
|
||||
|
||||
Triple(spaceView, spaceViewMembers, isCurrentUserOwner)
|
||||
Triple(spaceResponse, membersResponse, isCurrentUserOwner)
|
||||
}.catch {
|
||||
Timber.e(
|
||||
it, "Error while $SHARE_SPACE_MEMBER_SUBSCRIPTION " +
|
||||
"and $SHARE_SPACE_SPACE_SUBSCRIPTION subscription"
|
||||
)
|
||||
}.collect { (spaceView, spaceViewMembers, isCurrentUserOwner) ->
|
||||
}.collect { (spaceResponse, membersResponse, isCurrentUserOwner) ->
|
||||
val spaceView = spaceResponse.toSpaceView()
|
||||
val spaceMembers = membersResponse.toSpaceMembers()
|
||||
spaceAccessType.value = spaceView?.spaceAccessType
|
||||
setShareLinkViewState(spaceView, isCurrentUserOwner)
|
||||
members.value = spaceViewMembers
|
||||
members.value = spaceMembers.toView(
|
||||
spaceView = spaceView,
|
||||
urlBuilder = urlBuilder,
|
||||
isCurrentUserOwner = isCurrentUserOwner,
|
||||
account = account
|
||||
)
|
||||
showIncentive.value = spaceView?.getIncentiveState(
|
||||
spaceMembers = spaceMembers,
|
||||
isCurrentUserOwner = isCurrentUserOwner
|
||||
) ?: ShareSpaceIncentiveState.Hidden
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -441,6 +434,12 @@ class ShareSpaceViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun onIncentiveClicked() {
|
||||
viewModelScope.launch {
|
||||
commands.emit(Command.ShowMembershipScreen)
|
||||
}
|
||||
}
|
||||
|
||||
fun onDeleteLinkAccepted() {
|
||||
Timber.d("onDeleteLinkAccepted")
|
||||
viewModelScope.launch {
|
||||
|
@ -549,6 +548,13 @@ class ShareSpaceViewModel(
|
|||
data object ShowDeleteLinkWarning: Command()
|
||||
data object ToastPermission : Command()
|
||||
data object Dismiss : Command()
|
||||
data object ShowMembershipScreen : Command()
|
||||
}
|
||||
|
||||
sealed class ShareSpaceIncentiveState {
|
||||
data object Hidden : ShareSpaceIncentiveState()
|
||||
data object VisibleSpaceReaders : ShareSpaceIncentiveState()
|
||||
data object VisibleSpaceEditors : ShareSpaceIncentiveState()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.anytypeio.anytype.domain.base.fold
|
|||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.ApproveJoinSpaceRequest
|
||||
import com.anytypeio.anytype.domain.multiplayer.DeclineSpaceJoinRequest
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.`object`.canAddReaders
|
||||
import com.anytypeio.anytype.domain.`object`.canAddWriters
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
|
@ -33,6 +34,7 @@ import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
|||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.filterParticipants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -43,16 +45,20 @@ class SpaceJoinRequestViewModel(
|
|||
private val searchObjects: SearchObjects,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val analytics: Analytics
|
||||
private val analytics: Analytics,
|
||||
private val userPermissionProvider: UserPermissionProvider
|
||||
): BaseViewModel() {
|
||||
|
||||
val isDismissed = MutableStateFlow(false)
|
||||
private val _isCurrentUserOwner = MutableStateFlow(false)
|
||||
|
||||
private val state = MutableStateFlow<State>(State.Init)
|
||||
|
||||
val viewState = MutableStateFlow<ViewState>(ViewState.Init)
|
||||
|
||||
init {
|
||||
proceedWithUserPermissions()
|
||||
|
||||
viewModelScope.launch {
|
||||
val config = spaceManager.getConfig()
|
||||
if (config != null && config.space == params.space.id) {
|
||||
|
@ -119,7 +125,9 @@ class SpaceJoinRequestViewModel(
|
|||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
state.collect { curr ->
|
||||
state.combine(_isCurrentUserOwner) { state, isCurrentUserOwner ->
|
||||
state to isCurrentUserOwner
|
||||
}.collect { (curr, isCurrentUserOwner) ->
|
||||
viewState.value = when (curr) {
|
||||
is State.Error -> ViewState.Error
|
||||
is State.Init -> ViewState.Init
|
||||
|
@ -130,8 +138,8 @@ class SpaceJoinRequestViewModel(
|
|||
obj = curr.member,
|
||||
urlBuilder = urlBuilder
|
||||
),
|
||||
canAddAsReader = curr.spaceView.canAddReaders(curr.participants),
|
||||
canAddAsEditor = curr.spaceView.canAddWriters(curr.participants)
|
||||
canAddAsReader = curr.spaceView.canAddReaders(isCurrentUserOwner, curr.participants),
|
||||
canAddAsEditor = curr.spaceView.canAddWriters(isCurrentUserOwner, curr.participants)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +155,16 @@ class SpaceJoinRequestViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedWithUserPermissions() {
|
||||
viewModelScope.launch {
|
||||
userPermissionProvider
|
||||
.observe(space = params.space)
|
||||
.collect { permission ->
|
||||
_isCurrentUserOwner.value = permission == SpaceMemberPermissions.OWNER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getMembers(config: Config) {
|
||||
val searchMembersParams = SearchObjects.Params(
|
||||
filters = filterParticipants(
|
||||
|
@ -289,7 +307,8 @@ class SpaceJoinRequestViewModel(
|
|||
private val searchObjects: SearchObjects,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val analytics: Analytics
|
||||
private val analytics: Analytics,
|
||||
private val userPermissionProvider: UserPermissionProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = SpaceJoinRequestViewModel(
|
||||
|
@ -299,7 +318,8 @@ class SpaceJoinRequestViewModel(
|
|||
searchObjects = searchObjects,
|
||||
spaceManager = spaceManager,
|
||||
urlBuilder = urlBuilder,
|
||||
analytics = analytics
|
||||
analytics = analytics,
|
||||
userPermissionProvider = userPermissionProvider
|
||||
) as T
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import com.anytypeio.anytype.presentation.extension.getProperObjectName
|
|||
import com.anytypeio.anytype.presentation.library.LibraryView
|
||||
import com.anytypeio.anytype.presentation.linking.LinkToItemView
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.objects.SupportedLayouts.fileLayouts
|
||||
import com.anytypeio.anytype.presentation.relations.RelationValueView
|
||||
import com.anytypeio.anytype.presentation.sets.filter.CreateFilterView
|
||||
import com.anytypeio.anytype.presentation.widgets.collection.CollectionView
|
||||
|
@ -311,4 +310,25 @@ private fun ObjectWrapper.Basic.getFileObjectIcon(): ObjectIcon {
|
|||
|
||||
else -> ObjectIcon.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun List<ObjectWrapper.Basic>.toSpaceMembers(): List<ObjectWrapper.SpaceMember> =
|
||||
mapNotNull { basic ->
|
||||
if (basic.map.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
ObjectWrapper.SpaceMember(basic.map)
|
||||
}
|
||||
}
|
||||
|
||||
fun List<ObjectWrapper.Basic>.toSpaceView() =
|
||||
if (isNotEmpty()) {
|
||||
val spaceMap = first().map
|
||||
if (spaceMap.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
ObjectWrapper.SpaceView(spaceMap)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue