mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3659 Multiplayer | Invite link without approve (#2407)
This commit is contained in:
parent
b43fff9472
commit
300709aaa2
17 changed files with 375 additions and 133 deletions
|
@ -460,14 +460,10 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
|
|||
if (BuildConfig.DEBUG) Timber.d("Proceeding with share intent: $intent")
|
||||
when {
|
||||
intent.type == Mimetype.MIME_TEXT_PLAIN.value -> {
|
||||
val raw = intent.getStringExtra(Intent.EXTRA_TEXT) ?: intent.dataString
|
||||
if (raw != null) {
|
||||
if (checkDeepLink && DefaultDeepLinkResolver.isDeepLink(raw)) {
|
||||
vm.onNewDeepLink(DefaultDeepLinkResolver.resolve(raw))
|
||||
} else if (raw.isNotEmpty()) {
|
||||
vm.onIntentTextShare(raw)
|
||||
}
|
||||
}
|
||||
handleTextShare(
|
||||
intent = intent,
|
||||
checkDeepLink = checkDeepLink
|
||||
)
|
||||
}
|
||||
intent.type?.startsWith(SHARE_IMAGE_INTENT_PATTERN) == true -> {
|
||||
proceedWithImageShareIntent(intent)
|
||||
|
@ -485,6 +481,22 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleTextShare(intent: Intent, checkDeepLink: Boolean) {
|
||||
val raw = intent.getStringExtra(Intent.EXTRA_TEXT) ?: intent.dataString ?: return
|
||||
|
||||
when {
|
||||
checkDeepLink && DefaultDeepLinkResolver.isDeepLink(raw) -> {
|
||||
vm.onNewDeepLink(DefaultDeepLinkResolver.resolve(raw))
|
||||
}
|
||||
raw.isNotEmpty() && !DefaultDeepLinkResolver.isDeepLink(raw) -> {
|
||||
vm.onIntentTextShare(raw)
|
||||
}
|
||||
else -> {
|
||||
Timber.d("handleTextShare, skip handle intent :$raw")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithFileShareIntent(intent: Intent) {
|
||||
if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
|
||||
vm.onIntentMultipleFilesShare(intent.parseActionSendMultipleUris())
|
||||
|
|
|
@ -8,12 +8,17 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
@ -27,6 +32,8 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
|
||||
import com.anytypeio.anytype.core_models.multiplayer.MultiplayerError
|
||||
import com.anytypeio.anytype.core_ui.features.multiplayer.JoinSpaceScreen
|
||||
import com.anytypeio.anytype.core_ui.features.multiplayer.JoinSpaceWithoutApproveScreen
|
||||
import com.anytypeio.anytype.core_ui.features.multiplayer.JoiningLoadingState
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertConfig
|
||||
import com.anytypeio.anytype.core_ui.foundation.Announcement
|
||||
import com.anytypeio.anytype.core_ui.foundation.BUTTON_SECONDARY
|
||||
|
@ -40,7 +47,6 @@ import com.anytypeio.anytype.di.common.componentManager
|
|||
import com.anytypeio.anytype.presentation.common.TypedViewState
|
||||
import com.anytypeio.anytype.presentation.multiplayer.RequestJoinSpaceViewModel
|
||||
import com.anytypeio.anytype.presentation.multiplayer.RequestJoinSpaceViewModel.ErrorView
|
||||
import com.anytypeio.anytype.presentation.spaces.Command
|
||||
import com.anytypeio.anytype.ui.home.HomeScreenFragment
|
||||
import com.anytypeio.anytype.ui.notifications.NotificationPermissionPromptDialog
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
|
@ -89,39 +95,64 @@ class RequestJoinSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
}
|
||||
MaterialTheme(typography = typography) {
|
||||
val showModal = vm.showEnableNotificationDialog.collectAsStateWithLifecycle().value
|
||||
val isLoadingInvite = vm.showLoadingInviteProgress.collectAsStateWithLifecycle().value
|
||||
when(val state = vm.state.collectAsStateWithLifecycle().value) {
|
||||
is TypedViewState.Loading, is TypedViewState.Success -> {
|
||||
val isLoading: Boolean
|
||||
val spaceName: String
|
||||
val createdByName: String
|
||||
val withoutApprove : Boolean
|
||||
if (state is TypedViewState.Loading) {
|
||||
isLoading = true
|
||||
spaceName = stringResource(R.string.three_dots_text_placeholder)
|
||||
createdByName = stringResource(R.string.three_dots_text_placeholder)
|
||||
withoutApprove = false
|
||||
}
|
||||
else {
|
||||
isLoading = vm.isRequestInProgress.collectAsStateWithLifecycle().value
|
||||
with(state as TypedViewState.Success) {
|
||||
spaceName = state.data.spaceName
|
||||
createdByName = state.data.creatorName
|
||||
withoutApprove = state.data.withoutApprove
|
||||
}
|
||||
}
|
||||
if (!showModal) {
|
||||
JoinSpaceScreen(
|
||||
isLoading = isLoading,
|
||||
onRequestJoinSpaceClicked = vm::onRequestToJoinClicked,
|
||||
spaceName = spaceName,
|
||||
createdByName = createdByName
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
vm.onNotificationPromptDismissed()
|
||||
},
|
||||
dragHandle = {},
|
||||
containerColor = colorResource(id = R.color.background_secondary),
|
||||
sheetState = bottomSheetState
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
onDismissRequest = {
|
||||
if (!isLoadingInvite) {
|
||||
dismiss()
|
||||
}
|
||||
},
|
||||
dragHandle = {},
|
||||
containerColor = colorResource(id = R.color.background_secondary),
|
||||
sheetState = bottomSheetState
|
||||
) {
|
||||
if (isLoadingInvite) {
|
||||
JoiningLoadingState(
|
||||
onCancelLoadingInviteClicked = {
|
||||
vm.onCancelLoadingInviteClicked()
|
||||
dismiss()
|
||||
}
|
||||
)
|
||||
} else if (!showModal) {
|
||||
if (withoutApprove) {
|
||||
JoinSpaceWithoutApproveScreen(
|
||||
isLoading = isLoading,
|
||||
onRequestJoinSpaceClicked = vm::onRequestToJoinClicked,
|
||||
spaceName = spaceName,
|
||||
createdByName = createdByName
|
||||
)
|
||||
} else {
|
||||
JoinSpaceScreen(
|
||||
isLoading = isLoading,
|
||||
onRequestJoinSpaceClicked = vm::onRequestToJoinClicked,
|
||||
spaceName = spaceName,
|
||||
createdByName = createdByName,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Prompt(
|
||||
title = stringResource(R.string.notifications_prompt_get_notified),
|
||||
description = stringResource(R.string.notifications_prompt_description),
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.anytypeio.anytype.core_models.multiplayer
|
||||
|
||||
enum class InviteType(val code: Int) {
|
||||
MEMBER(0),
|
||||
GUEST(1),
|
||||
WITHOUT_APPROVE(2);
|
||||
}
|
|
@ -13,7 +13,8 @@ data class SpaceInviteView(
|
|||
val space: SpaceId,
|
||||
val spaceName: String,
|
||||
val creatorName: String,
|
||||
val spaceIconContentId: String
|
||||
val spaceIconContentId: String,
|
||||
val withoutApprove: Boolean
|
||||
)
|
||||
|
||||
enum class ParticipantStatus(
|
||||
|
|
|
@ -1,60 +1,35 @@
|
|||
package com.anytypeio.anytype.core_ui.features.multiplayer
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.ColorTextInputCursor
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.extensions.throttledClick
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertConfig
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertIcon
|
||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||
import com.anytypeio.anytype.core_ui.foundation.GRADIENT_TYPE_BLUE
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonPrimaryLoading
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSecondary
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Regular
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun JoinSpaceScreenPreview() {
|
||||
JoinSpaceScreen(
|
||||
onRequestJoinSpaceClicked = {},
|
||||
spaceName = "Anytype Android App",
|
||||
createdByName = "Konstantin"
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun JoinSpaceScreenPreviewWithEmptyNames() {
|
||||
JoinSpaceScreen(
|
||||
onRequestJoinSpaceClicked = {},
|
||||
spaceName = "",
|
||||
createdByName = ""
|
||||
)
|
||||
}
|
||||
import com.anytypeio.anytype.core_ui.views.Title2
|
||||
|
||||
@Composable
|
||||
fun JoinSpaceScreen(
|
||||
|
@ -64,7 +39,9 @@ fun JoinSpaceScreen(
|
|||
isLoading: Boolean = false
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Dragger(
|
||||
modifier = Modifier
|
||||
|
@ -72,22 +49,21 @@ fun JoinSpaceScreen(
|
|||
.padding(vertical = 6.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
AlertIcon(
|
||||
icon = AlertConfig.Icon(
|
||||
gradient = GRADIENT_TYPE_BLUE,
|
||||
icon = R.drawable.ic_alert_message
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
painter = painterResource(R.drawable.ic_join_without_approve),
|
||||
contentDescription = "Join without approve"
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.multiplayer_join_a_space),
|
||||
style = HeadlineHeading,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
style = BodyRegular,
|
||||
style = Title2,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(horizontal = 48.dp),
|
||||
text = stringResource(
|
||||
|
@ -97,9 +73,9 @@ fun JoinSpaceScreen(
|
|||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Spacer(modifier = Modifier.height(19.dp))
|
||||
Box(
|
||||
modifier = Modifier.padding(start = 20.dp, end = 20.dp)
|
||||
modifier = Modifier
|
||||
) {
|
||||
ButtonPrimaryLoading(
|
||||
onClick = throttledClick(
|
||||
|
@ -124,47 +100,153 @@ fun JoinSpaceScreen(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun CommentBox(commentInputValue: String): String {
|
||||
var commentInputValue1 = commentInputValue
|
||||
Box(
|
||||
fun JoinSpaceWithoutApproveScreen(
|
||||
onRequestJoinSpaceClicked: () -> Unit,
|
||||
spaceName: String,
|
||||
createdByName: String,
|
||||
isLoading: Boolean = false
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.height(128.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp)
|
||||
.border(width = 1.dp, color = colorResource(id = R.color.shape_primary))
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Dragger(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(vertical = 6.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
painter = painterResource(R.drawable.ic_join_without_approve),
|
||||
contentDescription = "Join without approve"
|
||||
)
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.multiplayer_private_comment_for_a_space_owner),
|
||||
style = Caption1Medium,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp, top = 10.dp)
|
||||
)
|
||||
TextField(
|
||||
value = commentInputValue1,
|
||||
onValueChange = { commentInputValue1 = it },
|
||||
textStyle = PreviewTitle2Regular.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
text = stringResource(
|
||||
R.string.multiplayer_request_to_join_without_approve_title,
|
||||
spaceName
|
||||
),
|
||||
style = HeadlineHeading,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 31.dp),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.multiplayer_tap_to_write_request_to_join_comment),
|
||||
color = colorResource(id = R.color.text_tertiary)
|
||||
)
|
||||
},
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
disabledBorderColor = Color.Transparent,
|
||||
errorBorderColor = Color.Transparent,
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
cursorColor = ColorTextInputCursor
|
||||
)
|
||||
.padding(horizontal = 16.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
style = Title2,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
text = stringResource(
|
||||
id = R.string.multiplayer_request_to_join_without_approve_desc,
|
||||
spaceName.ifEmpty { stringResource(id = R.string.untitled) },
|
||||
createdByName.ifEmpty { stringResource(id = R.string.untitled) }
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(19.dp))
|
||||
ButtonPrimaryLoading(
|
||||
onClick = throttledClick(
|
||||
onClick = { onRequestJoinSpaceClicked() }
|
||||
),
|
||||
size = ButtonSize.Large,
|
||||
text = stringResource(R.string.multiplayer_request_to_join_without_approve_button),
|
||||
modifierButton = Modifier.fillMaxWidth(),
|
||||
loading = isLoading
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
ButtonSecondary(
|
||||
onClick = throttledClick(
|
||||
onClick = { onRequestJoinSpaceClicked() }
|
||||
),
|
||||
text = stringResource(R.string.cancel),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
return commentInputValue1
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun JoiningLoadingState(
|
||||
onCancelLoadingInviteClicked: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(27.dp))
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.size(56.dp),
|
||||
color = colorResource(R.color.shape_secondary),
|
||||
trackColor = colorResource(R.color.shape_primary)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
textAlign = TextAlign.Center,
|
||||
text = stringResource(R.string.multiplayer_request_to_join_loading_text),
|
||||
style = Title2
|
||||
)
|
||||
Spacer(modifier = Modifier.height(19.dp))
|
||||
ButtonSecondary(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 12.dp, start = 16.dp, end = 16.dp),
|
||||
onClick = {
|
||||
onCancelLoadingInviteClicked()
|
||||
},
|
||||
size = ButtonSize.Large,
|
||||
text = stringResource(R.string.cancel),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DefaultPreviews
|
||||
fun JoinSpaceScreenPreviewLoading() {
|
||||
JoiningLoadingState(
|
||||
onCancelLoadingInviteClicked = {}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DefaultPreviews
|
||||
fun JoinSpaceScreenPreview() {
|
||||
JoinSpaceScreen(
|
||||
onRequestJoinSpaceClicked = {},
|
||||
spaceName = "Anytype Android App",
|
||||
createdByName = "Konstantin"
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DefaultPreviews
|
||||
fun JoinSpaceScreenPreviewWithEmptyNames() {
|
||||
JoinSpaceScreen(
|
||||
onRequestJoinSpaceClicked = {},
|
||||
spaceName = "",
|
||||
createdByName = ""
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DefaultPreviews
|
||||
fun JoinSpaceScreenPreviewWithoutApprove() {
|
||||
JoinSpaceWithoutApproveScreen(
|
||||
onRequestJoinSpaceClicked = {},
|
||||
spaceName = "",
|
||||
createdByName = ""
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<path
|
||||
android:pathData="M0.348,48.658C0.116,47.781 0,46.849 0,45.864V22.447C0,21.339 0.077,20.399 0.232,19.63C0.402,18.86 0.697,18.182 1.114,17.597C1.548,17.012 2.144,16.45 2.902,15.911L23.264,1.824C25.028,0.608 26.607,0 28,0C29.409,0 30.987,0.608 32.736,1.824L53.098,15.911C53.872,16.45 54.468,17.012 54.886,17.597C55.304,18.182 55.59,18.86 55.745,19.63C55.915,20.399 56,21.339 56,22.447V45.864C56,46.849 55.884,47.781 55.652,48.658L38.424,33.209L51.101,20.623C51.411,20.299 51.55,19.93 51.519,19.514C51.504,19.098 51.295,18.76 50.892,18.498L30.252,4.342C29.432,3.772 28.681,3.487 28,3.487C27.334,3.487 26.584,3.772 25.748,4.342L5.108,18.498C4.705,18.76 4.489,19.098 4.458,19.514C4.427,19.93 4.574,20.299 4.899,20.623L17.576,33.209L0.348,48.658ZM7.29,53C6.036,53 4.992,52.854 4.156,52.561C3.336,52.269 2.639,51.868 2.066,51.36L24.866,30.945C25.376,30.484 25.887,30.145 26.398,29.929C26.924,29.714 27.458,29.606 28,29.606C28.542,29.606 29.068,29.714 29.579,29.929C30.105,30.145 30.624,30.484 31.134,30.945L53.957,51.383C53.384,51.876 52.68,52.269 51.844,52.561C51.024,52.854 49.987,53 48.733,53H7.29Z"
|
||||
android:fillColor="@color/palette_system_sky"/>
|
||||
</vector>
|
|
@ -37,6 +37,7 @@ import com.anytypeio.anytype.core_models.membership.EmailVerificationStatus
|
|||
import com.anytypeio.anytype.core_models.membership.GetPaymentUrlResponse
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteLink
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteView
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
|
@ -910,8 +911,16 @@ class BlockDataRepository(
|
|||
remote.makeSpaceShareable(space)
|
||||
}
|
||||
|
||||
override suspend fun generateSpaceInviteLink(space: SpaceId): SpaceInviteLink {
|
||||
return remote.generateSpaceInviteLink(space)
|
||||
override suspend fun generateSpaceInviteLink(
|
||||
space: SpaceId,
|
||||
inviteType: InviteType,
|
||||
permissions: SpaceMemberPermissions
|
||||
): SpaceInviteLink {
|
||||
return remote.generateSpaceInviteLink(
|
||||
space = space,
|
||||
inviteType = inviteType,
|
||||
permissions = permissions
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun revokeSpaceInviteLink(space: SpaceId) {
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.anytypeio.anytype.core_models.membership.EmailVerificationStatus
|
|||
import com.anytypeio.anytype.core_models.membership.GetPaymentUrlResponse
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteLink
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteView
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
|
@ -398,7 +399,11 @@ interface BlockRemote {
|
|||
suspend fun deleteRelationOption(command: Command.DeleteRelationOptions)
|
||||
|
||||
suspend fun makeSpaceShareable(space: SpaceId)
|
||||
suspend fun generateSpaceInviteLink(space: SpaceId) : SpaceInviteLink
|
||||
suspend fun generateSpaceInviteLink(
|
||||
space: SpaceId,
|
||||
inviteType: InviteType,
|
||||
permissions: SpaceMemberPermissions
|
||||
): SpaceInviteLink
|
||||
suspend fun revokeSpaceInviteLink(space: SpaceId)
|
||||
suspend fun approveSpaceRequest(
|
||||
space: SpaceId,
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.anytypeio.anytype.core_models.membership.EmailVerificationStatus
|
|||
import com.anytypeio.anytype.core_models.membership.GetPaymentUrlResponse
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteLink
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteView
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
|
@ -44,6 +45,7 @@ import com.anytypeio.anytype.core_models.primitives.Space
|
|||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
|
||||
import com.anytypeio.anytype.domain.multiplayer.Permissions
|
||||
import com.anytypeio.anytype.domain.page.Redo
|
||||
import com.anytypeio.anytype.domain.page.Undo
|
||||
|
||||
|
@ -443,7 +445,11 @@ interface BlockRepository {
|
|||
suspend fun deleteRelationOption(command: Command.DeleteRelationOptions)
|
||||
|
||||
suspend fun makeSpaceShareable(space: SpaceId)
|
||||
suspend fun generateSpaceInviteLink(space: SpaceId) : SpaceInviteLink
|
||||
suspend fun generateSpaceInviteLink(
|
||||
space: SpaceId,
|
||||
inviteType: InviteType,
|
||||
permissions: SpaceMemberPermissions
|
||||
): SpaceInviteLink
|
||||
suspend fun revokeSpaceInviteLink(space: SpaceId)
|
||||
suspend fun approveSpaceRequest(
|
||||
space: SpaceId,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.anytypeio.anytype.domain.multiplayer
|
||||
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteLink
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
|
@ -8,10 +10,21 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
|||
import javax.inject.Inject
|
||||
|
||||
class GenerateSpaceInviteLink @Inject constructor(
|
||||
private val dispatchers: AppCoroutineDispatchers,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
private val repo: BlockRepository
|
||||
): ResultInteractor<SpaceId, SpaceInviteLink>(dispatchers.io) {
|
||||
override suspend fun doWork(params: SpaceId): SpaceInviteLink = repo.generateSpaceInviteLink(
|
||||
space = params
|
||||
) : ResultInteractor<GenerateSpaceInviteLink.Params, SpaceInviteLink>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params): SpaceInviteLink {
|
||||
return repo.generateSpaceInviteLink(
|
||||
space = params.space,
|
||||
inviteType = params.inviteType,
|
||||
permissions = params.permissions
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val space: SpaceId,
|
||||
val inviteType: InviteType,
|
||||
val permissions: SpaceMemberPermissions
|
||||
)
|
||||
}
|
|
@ -1357,6 +1357,13 @@
|
|||
<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>
|
||||
<string name="multiplayer_request_to_join_explanation">Once the space owner approves your request, you\'ll join the space with the access rights owner determined.</string>
|
||||
|
||||
<string name="multiplayer_request_to_join_without_approve_title">Join %1$s</string>
|
||||
<string name="multiplayer_request_to_join_without_approve_desc">You\'ve been invited to join %1$s, created by %2$s</string>
|
||||
<string name="multiplayer_request_to_join_without_approve_button">Join Space</string>
|
||||
|
||||
<string name="multiplayer_request_to_join_loading_text"> Hang tight — we’re setting things up for you. This should only take a moment.</string>
|
||||
|
||||
<string name="multiplayer_space_request_to_join">Request to join</string>
|
||||
<string name="multiplayer_space_request_to_join_msg">You\'ve been invited to join %1$s space, created by %2$s. Send a request so space owner can let you in.</string>
|
||||
<string name="multiplayer_tap_to_write_request_to_join_comment">Tap to write your comment</string>
|
||||
|
|
|
@ -38,6 +38,7 @@ import com.anytypeio.anytype.core_models.membership.EmailVerificationStatus
|
|||
import com.anytypeio.anytype.core_models.membership.GetPaymentUrlResponse
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteLink
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteView
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
|
@ -872,8 +873,16 @@ class BlockMiddleware(
|
|||
middleware.makeSpaceShareable(space = space)
|
||||
}
|
||||
|
||||
override suspend fun generateSpaceInviteLink(space: SpaceId): SpaceInviteLink {
|
||||
return middleware.generateSpaceInviteLink(space)
|
||||
override suspend fun generateSpaceInviteLink(
|
||||
space: SpaceId,
|
||||
inviteType: InviteType,
|
||||
permissions: SpaceMemberPermissions
|
||||
): SpaceInviteLink {
|
||||
return middleware.generateSpaceInviteLink(
|
||||
space = space,
|
||||
inviteType = inviteType,
|
||||
permissions = permissions
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun revokeSpaceInviteLink(space: SpaceId) {
|
||||
|
|
|
@ -47,6 +47,7 @@ import com.anytypeio.anytype.core_models.membership.EmailVerificationStatus
|
|||
import com.anytypeio.anytype.core_models.membership.GetPaymentUrlResponse
|
||||
import com.anytypeio.anytype.core_models.membership.Membership
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipTierData
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteLink
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceInviteView
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
|
@ -70,6 +71,7 @@ import com.anytypeio.anytype.middleware.mappers.toCoreLinkPreview
|
|||
import com.anytypeio.anytype.middleware.mappers.toCoreModel
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreModelSearchResults
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreModels
|
||||
import com.anytypeio.anytype.middleware.mappers.toMiddleware
|
||||
import com.anytypeio.anytype.middleware.mappers.toMiddlewareModel
|
||||
import com.anytypeio.anytype.middleware.mappers.toMw
|
||||
import com.anytypeio.anytype.middleware.mappers.toPayload
|
||||
|
@ -2388,16 +2390,22 @@ class Middleware @Inject constructor(
|
|||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun generateSpaceInviteLink(space: SpaceId) : SpaceInviteLink {
|
||||
fun generateSpaceInviteLink(
|
||||
space: SpaceId,
|
||||
inviteType: InviteType,
|
||||
permissions: SpaceMemberPermissions
|
||||
): SpaceInviteLink {
|
||||
val request = Rpc.Space.InviteGenerate.Request(
|
||||
spaceId = space.id
|
||||
spaceId = space.id,
|
||||
inviteType = inviteType.toMiddleware(),
|
||||
permissions = permissions.toMw()
|
||||
)
|
||||
logRequestIfDebug(request)
|
||||
val (response, time) = measureTimedValue { service.spaceInviteGenerate(request) }
|
||||
logResponseIfDebug(response, time)
|
||||
return SpaceInviteLink(
|
||||
contentId = response.inviteCid,
|
||||
fileKey= response.inviteFileKey
|
||||
fileKey = response.inviteFileKey
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2511,7 +2519,8 @@ class Middleware @Inject constructor(
|
|||
space = SpaceId(response.spaceId),
|
||||
creatorName = response.creatorName,
|
||||
spaceName = response.spaceName,
|
||||
spaceIconContentId = response.spaceIconCid
|
||||
spaceIconContentId = response.spaceIconCid,
|
||||
withoutApprove = response.inviteType == anytype.model.InviteType.WithoutApprove
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -114,4 +114,6 @@ typealias MSyncStatusUpdate = Space.SyncStatus.Update
|
|||
|
||||
typealias MDeviceNetworkType = anytype.model.DeviceNetworkType
|
||||
|
||||
typealias MLinkPreview = anytype.model.LinkPreview
|
||||
typealias MLinkPreview = anytype.model.LinkPreview
|
||||
|
||||
typealias MInviteType = anytype.model.InviteType
|
|
@ -18,6 +18,7 @@ import com.anytypeio.anytype.core_models.Relation
|
|||
import com.anytypeio.anytype.core_models.chats.Chat
|
||||
import com.anytypeio.anytype.core_models.membership.MembershipPaymentMethod
|
||||
import com.anytypeio.anytype.core_models.membership.NameServiceNameType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
|
||||
|
||||
|
||||
|
@ -637,3 +638,9 @@ fun DeviceNetworkType.mw(): MDeviceNetworkType = when(this) {
|
|||
DeviceNetworkType.NOT_CONNECTED -> MDeviceNetworkType.NOT_CONNECTED
|
||||
}
|
||||
|
||||
fun InviteType.toMiddleware(): MInviteType = when (this) {
|
||||
InviteType.MEMBER -> MInviteType.Member
|
||||
InviteType.GUEST -> MInviteType.Guest
|
||||
InviteType.WITHOUT_APPROVE -> MInviteType.WithoutApprove
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ import com.anytypeio.anytype.domain.workspace.SpaceManager
|
|||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import com.anytypeio.anytype.presentation.common.TypedViewState
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -50,6 +52,8 @@ class RequestJoinSpaceViewModel(
|
|||
val isRequestInProgress = MutableStateFlow(false)
|
||||
val showEnableNotificationDialog = MutableStateFlow(false)
|
||||
val commands = MutableSharedFlow<Command>(0)
|
||||
val showLoadingInviteProgress = MutableStateFlow(false)
|
||||
private var getSpaceInviteViewJob: Job? = null
|
||||
|
||||
init {
|
||||
Timber.i("RequestJoinSpaceViewModel, init")
|
||||
|
@ -60,7 +64,8 @@ class RequestJoinSpaceViewModel(
|
|||
val fileKey = spaceInviteResolver.parseFileKey(params.link)
|
||||
val contentId = spaceInviteResolver.parseContentId(params.link)
|
||||
if (fileKey != null && contentId != null) {
|
||||
viewModelScope.launch {
|
||||
showLoadingInviteProgress.value = true
|
||||
getSpaceInviteViewJob = viewModelScope.launch {
|
||||
getSpaceInviteView.async(
|
||||
GetSpaceInviteView.Params(
|
||||
inviteContentId = contentId,
|
||||
|
@ -68,6 +73,7 @@ class RequestJoinSpaceViewModel(
|
|||
)
|
||||
).fold(
|
||||
onSuccess = { view ->
|
||||
showLoadingInviteProgress.value = false
|
||||
val isAlreadyMember = checkIsUserSpaceMember
|
||||
.async(view.space)
|
||||
.getOrDefault(false)
|
||||
|
@ -85,6 +91,7 @@ class RequestJoinSpaceViewModel(
|
|||
}
|
||||
},
|
||||
onFailure = { e ->
|
||||
showLoadingInviteProgress.value = false
|
||||
if (e is SpaceInviteError) {
|
||||
when(e) {
|
||||
is SpaceInviteError.InvalidInvite -> {
|
||||
|
@ -117,6 +124,11 @@ class RequestJoinSpaceViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun onCancelLoadingInviteClicked() {
|
||||
getSpaceInviteViewJob?.cancel()
|
||||
showLoadingInviteProgress.value = false
|
||||
}
|
||||
|
||||
fun onRequestToJoinClicked() {
|
||||
when(val curr = state.value) {
|
||||
is TypedViewState.Success -> {
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.ext.isPossibleToUpgradeNumberOfSpaceMembers
|
||||
import com.anytypeio.anytype.core_models.membership.TierId
|
||||
import com.anytypeio.anytype.core_models.multiplayer.InviteType
|
||||
import com.anytypeio.anytype.core_models.multiplayer.MultiplayerError
|
||||
import com.anytypeio.anytype.core_models.multiplayer.ParticipantStatus
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
|
||||
|
@ -203,7 +204,10 @@ class ShareSpaceViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedWithGeneratingInviteLink() {
|
||||
private fun proceedWithGeneratingInviteLink(
|
||||
inviteType: InviteType = InviteType.MEMBER,
|
||||
permissions: SpaceMemberPermissions = SpaceMemberPermissions.READER
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
if (spaceAccessType.value == SpaceAccessType.PRIVATE) {
|
||||
makeSpaceShareable.async(
|
||||
|
@ -212,27 +216,44 @@ class ShareSpaceViewModel(
|
|||
onSuccess = {
|
||||
analytics.sendEvent(eventName = EventsDictionary.shareSpace)
|
||||
Timber.d("Successfully made space shareable")
|
||||
generateInviteLink(
|
||||
inviteType = inviteType,
|
||||
permissions = permissions
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while making space shareable")
|
||||
proceedWithMultiplayerError(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
generateSpaceInviteLink
|
||||
.async(vmParams.space)
|
||||
.fold(
|
||||
onSuccess = { link ->
|
||||
shareLinkViewState.value = ShareLinkViewState.Shared(link = link.scheme)
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while generating invite link")
|
||||
proceedWithMultiplayerError(it)
|
||||
}
|
||||
} else {
|
||||
generateInviteLink(
|
||||
inviteType = inviteType,
|
||||
permissions = permissions
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun generateInviteLink(inviteType: InviteType, permissions: SpaceMemberPermissions) {
|
||||
generateSpaceInviteLink.async(
|
||||
params = GenerateSpaceInviteLink.Params(
|
||||
space = vmParams.space,
|
||||
inviteType = inviteType,
|
||||
permissions = permissions
|
||||
)
|
||||
).fold(
|
||||
onSuccess = { inviteLink ->
|
||||
shareLinkViewState.value = ShareLinkViewState.Shared(inviteLink.scheme)
|
||||
Timber.d("Successfully generated invite link")
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while generating invite link")
|
||||
proceedWithMultiplayerError(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun onShareInviteLinkClicked() {
|
||||
viewModelScope.launch {
|
||||
when (val value = shareLinkViewState.value) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue