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

DROID-3375 New Settings | Fix | Basics, part 1 (#2140)

This commit is contained in:
Evgenii Kozlov 2025-03-13 13:53:18 +01:00 committed by GitHub
parent 0f03390ae4
commit d19f5a0ba4
Signed by: github
GPG key ID: B5690EEEBB952194
8 changed files with 453 additions and 306 deletions

View file

@ -16,10 +16,7 @@ import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
import com.anytypeio.anytype.core_ui.extensions.throttledClick
import com.anytypeio.anytype.core_utils.clipboard.copyPlainTextToClipboard
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.parseImagePath
import com.anytypeio.anytype.core_utils.ext.shareFile
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
@ -29,14 +26,14 @@ import com.anytypeio.anytype.presentation.spaces.SpaceSettingsViewModel.Command
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
import com.anytypeio.anytype.ui.multiplayer.LeaveSpaceWarning
import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
import com.anytypeio.anytype.ui.settings.SpacesStorageFragment
import com.anytypeio.anytype.ui.settings.typography
import com.anytypeio.anytype.ui.spaces.DeleteSpaceWarning
import com.anytypeio.anytype.ui_settings.space.SpaceSettingsScreen
import com.anytypeio.anytype.ui_settings.space.new_settings.SpaceSettingsContainer
import java.io.File
import javax.inject.Inject
import timber.log.Timber
// TODO convert to Fragment.
class SpaceSettingsFragment : BaseBottomSheetComposeFragment() {
private val space get() = arg<Id>(ARG_SPACE_ID_KEY)
@ -62,60 +59,9 @@ class SpaceSettingsFragment : BaseBottomSheetComposeFragment() {
surface = colorResource(id = R.color.context_menu_background)
)
) {
SpaceSettingsScreen(
onNameSet = vm::onNameSet,
state = vm.spaceViewState.collectAsStateWithLifecycle().value,
onDeleteSpaceClicked = throttledClick(
onClick = { vm.onDeleteSpaceClicked() }
),
onFileStorageClick = throttledClick(
onClick = {
findNavController().navigate(
resId = R.id.spacesStorageScreen,
args = SpacesStorageFragment.args(space = space)
)
}
),
onPersonalizationClicked = throttledClick(
onClick = {
findNavController().navigate(R.id.personalizationScreen)
}
),
onSpaceIdClicked = {
context.copyPlainTextToClipboard(
plainText = it,
label = "Space ID",
successToast = context.getString(R.string.space_id_copied_toast_msg)
)
},
onNetworkIdClicked = {
context.copyPlainTextToClipboard(
plainText = it,
label = "Network ID",
successToast = context.getString(R.string.network_id_copied_toast_msg)
)
},
onCreatedByClicked = {
context.copyPlainTextToClipboard(
plainText = it,
label = "Created-by ID",
successToast = context.getString(R.string.created_by_id_copied_toast_msg)
)
},
onDebugClicked = vm::onSpaceDebugClicked,
onRemoveIconClicked = vm::onRemoveSpaceIconClicked,
onManageSharedSpaceClicked = vm::onManageSharedSpaceClicked,
onSharePrivateSpaceClicked = vm::onSharePrivateSpaceClicked,
onAddMoreSpacesClicked = vm::onAddMoreSpacesClicked,
onSpaceImagePicked = { uri ->
runCatching {
vm.onSpaceImagePicked(
path = uri.parseImagePath(requireContext())
)
}.onFailure {
toast(getString(R.string.error_while_loading_picture))
}
}
SpaceSettingsContainer(
uiState = vm.uiState.collectAsStateWithLifecycle().value,
uiEvent = vm::onUiEvent
)
LaunchedEffect(Unit) { vm.toasts.collect { toast(it) } }
LaunchedEffect(Unit) {

View file

@ -316,7 +316,10 @@ fun SpaceSettingsScreenPreview() {
shareLimitReached = SpaceSettingsViewModel.ShareLimitsState(
shareLimitReached = false,
sharedSpacesLimit = 0
)
),
isUserOwner = false,
isEditEnabled = false,
description = ""
)
),
onNameSet = {},

View file

@ -27,6 +27,8 @@ import com.anytypeio.anytype.core_ui.features.SpaceIconView
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.Caption1Medium
import com.anytypeio.anytype.core_utils.ext.parseImagePath
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
import com.anytypeio.anytype.presentation.spaces.UiEvent
import com.anytypeio.anytype.ui_settings.R
@ -40,16 +42,23 @@ fun NewSpaceIcon(
isEditEnabled: Boolean
) {
val context = LocalContext.current
val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia(),
onResult = { uri ->
if (uri != null) {
uiEvent(UiEvent.OnSpaceImagePicked(uri.toString()))
runCatching {
val path = uri.parseImagePath(context)
uiEvent(UiEvent.OnSpaceImagePicked(path))
}.onFailure {
context.toast(context.getString(R.string.error_while_loading_picture))
}
} else {
Timber.w("Uri was null after picking image")
}
}
)
val isSpaceIconMenuExpanded = remember {
mutableStateOf(false)
}

View file

@ -61,6 +61,7 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.dropWhile
import kotlinx.coroutines.flow.filter
import timber.log.Timber
@Composable
fun MembersItem(
@ -249,18 +250,24 @@ fun BaseButton(
}
}
@OptIn(FlowPreview::class)
@Composable
fun NewSpaceNameBlock(
fun NewSpaceNameInputField(
modifier: Modifier = Modifier,
name: String,
onNameSet: (String) -> Unit,
isEditEnabled: Boolean
isEditEnabled: Boolean,
onNameSet: (String) -> Unit = {}
) {
val nameValue = remember { mutableStateOf(name) }
val focusManager = LocalFocusManager.current
LaunchedEffect(name) {
nameValue.value = name
}
LaunchedEffect(nameValue.value) {
snapshotFlow { nameValue.value }
.debounce(300L)
@ -300,13 +307,17 @@ fun NewSpaceNameBlock(
fun NewSpaceDescriptionBlock(
modifier: Modifier = Modifier,
description: String,
onDescriptionSet: (String) -> Unit,
isEditEnabled: Boolean
isEditEnabled: Boolean,
onDescriptionSet: (String) -> Unit = {},
) {
val descriptionValue = remember { mutableStateOf(description) }
val focusManager = LocalFocusManager.current
LaunchedEffect(description) {
descriptionValue.value = description
}
LaunchedEffect(descriptionValue.value) {
snapshotFlow { descriptionValue.value }
.debounce(300L)

View file

@ -3,11 +3,9 @@ package com.anytypeio.anytype.ui_settings.space.new_settings
import android.os.Build
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@ -16,11 +14,11 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -33,8 +31,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.PreviewTitle1Medium
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
@ -42,6 +40,7 @@ import com.anytypeio.anytype.presentation.spaces.UiEvent
import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsItem
import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsState
import com.anytypeio.anytype.ui_settings.R
import timber.log.Timber
@Composable
fun SpaceSettingsContainer(
@ -56,24 +55,17 @@ fun SpaceSettingsContainer(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NewSpaceSettingsScreen(
uiState: UiSpaceSettingsState.SpaceSettings,
uiEvent: (UiEvent) -> Unit
) {
val initialName = uiState.name
val initialDescription = uiState.description
// Get the initial values from your uiState items.
val initialName = uiState.items.filterIsInstance<UiSpaceSettingsItem.Name>()
.firstOrNull()?.name ?: ""
val initialDescription = uiState.items.filterIsInstance<UiSpaceSettingsItem.Description>()
.firstOrNull()?.description ?: ""
// Keep state of the current (edited) values.
var nameInput by remember { mutableStateOf(initialName) }
var descriptionInput by remember { mutableStateOf(initialDescription) }
// Compare against the initial values to know if something has changed.
val isDirty = nameInput != initialName || descriptionInput != initialDescription
var showEditDescription by remember { mutableStateOf(false) }
var showEditTitle by remember { mutableStateOf(false) }
Scaffold(
modifier = Modifier.fillMaxWidth(),
@ -102,29 +94,6 @@ fun NewSpaceSettingsScreen(
uiEvent(UiEvent.OnBackPressed)
}
)
Box(
modifier = Modifier
.wrapContentWidth()
.fillMaxHeight()
.align(Alignment.CenterEnd)
.clickable(enabled = isDirty) {
uiEvent(UiEvent.OnSavedClicked(nameInput, descriptionInput))
},
contentAlignment = Alignment.Center
) {
Text(
modifier = Modifier
.wrapContentSize()
.padding(horizontal = 16.dp),
text = stringResource(R.string.space_settings_save_button),
style = PreviewTitle1Medium,
color = if (isDirty) {
colorResource(id = R.color.text_primary)
} else {
colorResource(id = R.color.text_tertiary)
}
)
}
}
},
content = { paddingValues ->
@ -160,10 +129,9 @@ fun NewSpaceSettingsScreen(
)
}
}
is UiSpaceSettingsItem.Name -> {
item {
NewSpaceNameBlock(
NewSpaceNameInputField(
modifier = Modifier
.fillMaxWidth()
.border(
@ -172,12 +140,13 @@ fun NewSpaceSettingsScreen(
color = colorResource(id = R.color.shape_primary)
)
.padding(vertical = 12.dp, horizontal = 16.dp)
.animateItem(),
name = nameInput,
onNameSet = { newName ->
nameInput = newName
},
isEditEnabled = uiState.isEditEnabled
.animateItem()
.noRippleClickable {
showEditTitle = true
}
,
name = item.name,
isEditEnabled = false
)
}
}
@ -193,12 +162,13 @@ fun NewSpaceSettingsScreen(
color = colorResource(id = R.color.shape_primary)
)
.padding(vertical = 12.dp, horizontal = 16.dp)
.animateItem(),
isEditEnabled = uiState.isEditEnabled,
description = descriptionInput,
onDescriptionSet = { newDescription ->
descriptionInput = newDescription
}
.animateItem()
.noRippleClickable {
showEditDescription = true
}
,
isEditEnabled = false,
description = initialDescription
)
}
}
@ -213,7 +183,9 @@ fun NewSpaceSettingsScreen(
}
}
is UiSpaceSettingsItem.Chat -> TODO()
is UiSpaceSettingsItem.Chat -> {
// TODO
}
is UiSpaceSettingsItem.DefaultObjectType -> {
item {
DefaultTypeItem(
@ -257,7 +229,9 @@ fun NewSpaceSettingsScreen(
}
}
is UiSpaceSettingsItem.RemoteStorage -> TODO()
is UiSpaceSettingsItem.RemoteStorage -> {
// TODO
}
is UiSpaceSettingsItem.Section -> {
item {
SpaceSettingsSection(
@ -302,4 +276,184 @@ fun NewSpaceSettingsScreen(
}
)
if (showEditDescription) {
ModalBottomSheet(
containerColor = colorResource(R.color.background_secondary),
onDismissRequest = {
showEditDescription = false
},
dragHandle = {
Dragger(
modifier = Modifier.padding(vertical = 6.dp)
)
},
) {
EditDescriptionField(
initialInput = initialDescription,
onSaveFieldValueClicked = {
uiEvent(UiEvent.OnSaveDescriptionClicked(it)).also {
showEditDescription = false
}
}
)
}
}
if (showEditTitle) {
ModalBottomSheet(
containerColor = colorResource(R.color.background_secondary),
onDismissRequest = {
showEditTitle = false
},
dragHandle = {
Dragger(
modifier = Modifier.padding(vertical = 6.dp)
)
}
) {
EditNameField(
initialInput = initialName,
onSaveFieldValueClicked = {
uiEvent(UiEvent.OnSaveTitleClicked(it)).also {
showEditTitle = false
}
}
)
}
}
}
@Composable
private fun EditNameField(
initialInput: String,
onSaveFieldValueClicked: (String) -> Unit
) {
var fieldInput by remember { mutableStateOf(initialInput) }
Scaffold(
modifier = Modifier.fillMaxWidth(),
containerColor = colorResource(id = R.color.background_secondary),
topBar = {
Box(modifier = Modifier.fillMaxWidth().height(48.dp)) {
Text(
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(horizontal = 16.dp)
.noRippleClickable {
onSaveFieldValueClicked(fieldInput)
},
text = "Done",
style = PreviewTitle1Medium,
color = if (fieldInput != initialInput) {
colorResource(id = R.color.text_primary)
} else {
colorResource(id = R.color.text_tertiary)
}
)
}
},
content = { paddingValues ->
val contentModifier =
if (Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
Modifier
.padding(top = paddingValues.calculateTopPadding())
.windowInsetsPadding(WindowInsets.navigationBars)
.fillMaxSize()
else
Modifier
.fillMaxSize()
.padding(paddingValues)
Box(
modifier = contentModifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
NewSpaceNameInputField(
modifier = Modifier
.fillMaxWidth()
.border(
shape = RoundedCornerShape(16.dp),
width = 2.dp,
color = colorResource(id = R.color.palette_system_amber_50)
)
.padding(vertical = 12.dp, horizontal = 16.dp)
,
name = fieldInput,
onNameSet = { newName ->
fieldInput = newName
},
isEditEnabled = true
)
}
}
)
}
@Composable
private fun EditDescriptionField(
initialInput: String,
onSaveFieldValueClicked: (String) -> Unit
) {
var fieldInput by remember { mutableStateOf(initialInput) }
Scaffold(
modifier = Modifier.fillMaxWidth(),
containerColor = colorResource(id = R.color.background_secondary),
topBar = {
Box(modifier = Modifier.fillMaxWidth().height(48.dp)) {
Text(
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(horizontal = 16.dp)
.noRippleClickable {
onSaveFieldValueClicked(fieldInput)
},
text = "Done",
style = PreviewTitle1Medium,
color = if (fieldInput != initialInput) {
colorResource(id = R.color.text_primary)
} else {
colorResource(id = R.color.text_tertiary)
}
)
}
},
content = { paddingValues ->
val contentModifier =
if (Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
Modifier
.windowInsetsPadding(WindowInsets.navigationBars)
.fillMaxSize()
.padding(top = paddingValues.calculateTopPadding())
else
Modifier
.fillMaxSize()
.padding(paddingValues)
Box(
modifier = contentModifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
NewSpaceDescriptionBlock(
modifier = Modifier
.fillMaxWidth()
.border(
shape = RoundedCornerShape(16.dp),
width = 2.dp,
color = colorResource(id = R.color.palette_system_amber_50)
)
.padding(vertical = 12.dp, horizontal = 16.dp)
,
description = fieldInput,
isEditEnabled = true,
onDescriptionSet = {
fieldInput = it
}
)
}
}
)
}

View file

@ -5,7 +5,6 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.analytics.base.EventsDictionary.screenLeaveSpace
import com.anytypeio.anytype.analytics.base.EventsPropertiesKey
import com.anytypeio.anytype.analytics.base.sendEvent
import com.anytypeio.anytype.analytics.props.Props
@ -13,19 +12,11 @@ import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Filepath
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.PRIVATE_SPACE_TYPE
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SHARED_SPACE_TYPE
import com.anytypeio.anytype.core_models.SpaceType
import com.anytypeio.anytype.core_models.UNKNOWN_SPACE_TYPE
import com.anytypeio.anytype.core_models.asSpaceType
import com.anytypeio.anytype.core_models.ext.isPossibleToUpgrade
import com.anytypeio.anytype.core_models.membership.MembershipUpgradeReason
import com.anytypeio.anytype.core_models.membership.TierId
import com.anytypeio.anytype.core_models.multiplayer.ParticipantStatus
import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_utils.ui.ViewState
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
@ -41,6 +32,7 @@ import com.anytypeio.anytype.domain.spaces.DeleteSpace
import com.anytypeio.anytype.domain.spaces.SetSpaceDetails
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsItem.Spacer
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@ -70,13 +62,13 @@ class SpaceSettingsViewModel(
val commands = MutableSharedFlow<Command>()
val isDismissed = MutableStateFlow(false)
val spaceViewState = MutableStateFlow<ViewState<SpaceData>>(ViewState.Init)
val uiState = MutableStateFlow<UiSpaceSettingsState>(UiSpaceSettingsState.Initial)
val permissions = MutableStateFlow(SpaceMemberPermissions.NO_PERMISSIONS)
private val spaceConfig = spaceManager.getConfig()
init {
Timber.d("SpaceSettingsViewModel, Init, vmParams: $params")
viewModelScope.launch {
analytics.sendEvent(
eventName = EventsDictionary.screenSettingSpacesSpaceIndex
@ -87,7 +79,6 @@ class SpaceSettingsViewModel(
private fun proceedWithObservingSpaceView() {
viewModelScope.launch {
val config = spaceManager.getConfig(params.space)
combine(
spaceViewContainer.observe(params.space),
userPermissionProvider.observe(params.space),
@ -95,7 +86,7 @@ class SpaceSettingsViewModel(
.observe()
.map { wrapper ->
wrapper.getValue<Double?>(Relations.SHARED_SPACES_LIMIT)?.toInt() ?: 0
},
},
spaceViewContainer.sharedSpaceCount(userPermissionProvider.all()),
activeSpaceMemberSubscriptionContainer.observe(params.space),
) { spaceView, permission, sharedSpaceLimit: Int, sharedSpaceCount: Int, store ->
@ -111,56 +102,98 @@ class SpaceSettingsViewModel(
null
}
val createdBy = spaceMember?.globalName?.takeIf { it.isNotEmpty() } ?: spaceMember?.identity
SpaceData(
name = spaceView.name.orEmpty(),
icon = spaceView.spaceIcon(
builder = urlBuilder,
spaceGradientProvider = gradientProvider
//todo add logic
val membersNumber = 3
UiSpaceSettingsState.SpaceSettings(
items = listOf(
UiSpaceSettingsItem.Icon(
icon = spaceView.spaceIcon(
builder = urlBuilder,
spaceGradientProvider = gradientProvider
)
),
UiSpaceSettingsItem.Spacer(height = 16),
UiSpaceSettingsItem.Name(
//todo support localization for empty space name
name = spaceView.name.orEmpty()
),
UiSpaceSettingsItem.Spacer(height = 12),
UiSpaceSettingsItem.Description(
//todo support localization for empty space description
description = spaceView.description.orEmpty()
),
UiSpaceSettingsItem.Spacer(height = 12),
UiSpaceSettingsItem.Multiplayer,
UiSpaceSettingsItem.Spacer(height = 8),
UiSpaceSettingsItem.Section.Collaboration,
UiSpaceSettingsItem.Members(count = membersNumber),
),
createdDateInMillis = spaceView
.getValue<Double?>(Relations.CREATED_DATE)
?.let { timeInSeconds -> (timeInSeconds * 1000L).toLong() },
createdBy = createdBy,
spaceId = params.space.id,
network = config?.network.orEmpty(),
isDeletable = resolveIsSpaceDeletable(spaceView),
spaceType = spaceView.spaceAccessType?.asSpaceType() ?: UNKNOWN_SPACE_TYPE,
permissions = permission ?: SpaceMemberPermissions.NO_PERMISSIONS,
shareLimitReached = ShareLimitsState(
shareLimitReached = sharedSpaceCount >= sharedSpaceLimit,
sharedSpacesLimit = sharedSpaceLimit
),
requests = requests
isEditEnabled = permission?.isOwnerOrEditor() == true
)
}.collect { spaceData ->
Timber.d("Space data: ${spaceData}")
spaceViewState.value = ViewState.Success(spaceData)
uiState.value = spaceData
//spaceViewState.value = spaceData
}
}
}
fun onNameSet(name: String) {
Timber.d("onNameSet")
if (name.isEmpty()) return
if (isDismissed.value) return
viewModelScope.launch {
if (spaceConfig != null) {
setSpaceDetails.async(
SetSpaceDetails.Params(
space = SpaceId(spaceConfig.space),
details = mapOf(Relations.NAME to name)
fun onUiEvent(uiEvent: UiEvent) {
Timber.d("onUiEvent: $uiEvent")
when(uiEvent) {
UiEvent.IconMenu.OnRemoveIconClicked -> {
proceedWithRemovingSpaceIcon()
}
UiEvent.OnBackPressed -> {
isDismissed.value = true
}
UiEvent.OnDeleteSpaceClicked -> {
sendToast("Coming soon")
}
UiEvent.OnFileStorageClick -> {
sendToast("Coming soon")
}
UiEvent.OnInviteClicked -> {
sendToast("Coming soon")
}
UiEvent.OnPersonalizationClicked -> {
sendToast("Coming soon")
}
UiEvent.OnQrCodeClicked -> {
sendToast("Coming soon")
}
is UiEvent.OnSaveDescriptionClicked -> {
viewModelScope.launch {
setSpaceDetails.async(
params = SetSpaceDetails.Params(
space = params.space,
details = mapOf(
Relations.DESCRIPTION to uiEvent.description
)
)
)
).fold(
onFailure = {
Timber.e(it, "Error while updating object details")
sendToast("Something went wrong. Please try again")
},
onSuccess = {
Timber.d("Name successfully set for current space: ${spaceConfig.space}")
}
)
} else {
Timber.w("Something went wrong: config is empty")
}
}
is UiEvent.OnSaveTitleClicked -> {
viewModelScope.launch {
setSpaceDetails.async(
params = SetSpaceDetails.Params(
space = params.space,
details = mapOf(
Relations.NAME to uiEvent.title
)
)
)
}
}
UiEvent.OnSpaceIdClicked -> {
sendToast("Coming soon")
}
is UiEvent.OnSpaceImagePicked -> {
proceedWithSettingSpaceImage(uiEvent.uri)
}
}
}
@ -169,44 +202,39 @@ class SpaceSettingsViewModel(
// TODO unsubscribe
}
fun onSpaceDebugClicked() {
proceedWithSpaceDebug()
}
fun onRemoveSpaceIconClicked() {
// fun onSpaceDebugClicked() {
// proceedWithSpaceDebug()
// }
//
private fun proceedWithRemovingSpaceIcon() {
viewModelScope.launch {
val config = spaceConfig
if (config != null) {
setSpaceDetails.async(
SetSpaceDetails.Params(
space = params.space,
details = mapOf(
Relations.ICON_OPTION to spaceGradientProvider.randomId().toDouble(),
Relations.ICON_IMAGE to "",
)
setSpaceDetails.async(
SetSpaceDetails.Params(
space = params.space,
details = mapOf(
Relations.ICON_OPTION to spaceGradientProvider.randomId().toDouble(),
Relations.ICON_IMAGE to "",
)
)
}
)
}
}
fun onDeleteSpaceClicked() {
viewModelScope.launch {
val state = spaceViewState.value
if (state is ViewState.Success) {
if (state.data.permissions.isOwner()) {
commands.emit(Command.ShowDeleteSpaceWarning)
analytics.sendEvent(
eventName = EventsDictionary.clickDeleteSpace,
props = Props(mapOf(EventsPropertiesKey.route to EventsDictionary.Routes.settings))
)
} else {
commands.emit(Command.ShowLeaveSpaceWarning)
analytics.sendEvent(eventName = screenLeaveSpace)
}
}
}
}
// fun onDeleteSpaceClicked() {
// viewModelScope.launch {
// val state = spaceViewState.value as? SpaceData.Success ?: return@launch
// if (state.isUserOwner) {
// commands.emit(Command.ShowDeleteSpaceWarning)
// analytics.sendEvent(
// eventName = EventsDictionary.clickDeleteSpace,
// props = Props(mapOf(EventsPropertiesKey.route to EventsDictionary.Routes.settings))
// )
// } else {
// commands.emit(Command.ShowLeaveSpaceWarning)
// analytics.sendEvent(eventName = screenLeaveSpace)
// }
// }
// }
fun onDeleteSpaceWarningCancelled() {
viewModelScope.launch {
@ -243,29 +271,27 @@ class SpaceSettingsViewModel(
}
private fun proceedWithSpaceDeletion() {
val state = spaceViewState.value
if (state is ViewState.Success) {
val space = state.data.spaceId
if (space != null) {
viewModelScope.launch {
deleteSpace.async(params = SpaceId(space)).fold(
onSuccess = {
analytics.sendEvent(
eventName = EventsDictionary.deleteSpace,
props = Props(mapOf(EventsPropertiesKey.type to "Private"))
)
spaceManager.clear()
commands.emit(Command.ExitToVault)
},
onFailure = {
Timber.e(it, "Error while deleting space")
}
)
}
} else {
sendToast("Space not found. Please, try again later")
}
}
// val state = spaceViewState.value as? SpaceData.Success ?: return
// val space = state.spaceId
// if (space != null) {
// viewModelScope.launch {
// deleteSpace.async(params = SpaceId(space)).fold(
// onSuccess = {
// analytics.sendEvent(
// eventName = EventsDictionary.deleteSpace,
// props = Props(mapOf(EventsPropertiesKey.type to "Private"))
// )
// spaceManager.clear()
// commands.emit(Command.ExitToVault)
// },
// onFailure = {
// Timber.e(it, "Error while deleting space")
// }
// )
// }
// } else {
// sendToast("Space not found. Please, try again later")
// }
}
private fun proceedWithSpaceDebug() {
@ -289,78 +315,69 @@ class SpaceSettingsViewModel(
}
}
fun onSharePrivateSpaceClicked() {
viewModelScope.launch {
val data = spaceViewState.value
when(data) {
is ViewState.Success -> {
when(data.data.spaceType) {
PRIVATE_SPACE_TYPE -> {
analytics.sendEvent(
eventName = EventsDictionary.screenSettingsSpaceShare,
props = Props(
mapOf(
EventsPropertiesKey.route to EventsDictionary.Routes.settings
)
)
)
}
SHARED_SPACE_TYPE -> {
analytics.sendEvent(
eventName = EventsDictionary.screenSettingsSpaceMembers,
props = Props(
mapOf(
EventsPropertiesKey.route to EventsDictionary.Routes.settings
)
)
)
}
}
}
else -> {
// Do nothing.
}
}
}
viewModelScope.launch {
val data = spaceViewState.value
if (data is ViewState.Success) {
val shareLimits = data.data.shareLimitReached
if (!shareLimits.shareLimitReached) {
commands.emit(Command.SharePrivateSpace(params.space))
} else {
commands.emit(Command.ShowShareLimitReachedError)
}
}
}
}
// fun onSharePrivateSpaceClicked() {
// viewModelScope.launch {
// val data = spaceViewState.value as? SpaceData.Success ?: return@launch
// when(data.spaceType) {
// PRIVATE_SPACE_TYPE -> {
// analytics.sendEvent(
// eventName = EventsDictionary.screenSettingsSpaceShare,
// props = Props(
// mapOf(
// EventsPropertiesKey.route to EventsDictionary.Routes.settings
// )
// )
// )
// }
// SHARED_SPACE_TYPE -> {
// analytics.sendEvent(
// eventName = EventsDictionary.screenSettingsSpaceMembers,
// props = Props(
// mapOf(
// EventsPropertiesKey.route to EventsDictionary.Routes.settings
// )
// )
// )
// }
// }
// }
// viewModelScope.launch {
// val data = spaceViewState.value as? SpaceData.Success ?: return@launch
// val shareLimits = data.shareLimitReached
// if (!shareLimits.shareLimitReached) {
// commands.emit(Command.SharePrivateSpace(params.space))
// } else {
// commands.emit(Command.ShowShareLimitReachedError)
// }
// }
// }
private fun resolveIsSpaceDeletable(spaceView: ObjectWrapper.SpaceView) : Boolean {
return spaceView.spaceAccessType != null
}
fun onAddMoreSpacesClicked() {
viewModelScope.launch {
getMembership.async(GetMembershipStatus.Params(noCache = false)).fold(
onSuccess = { membership ->
if (membership != null) {
val activeTier = TierId(membership.tier)
if (activeTier.isPossibleToUpgrade(reason = MembershipUpgradeReason.NumberOfSharedSpaces)) {
commands.emit(Command.NavigateToMembership)
} else {
commands.emit(Command.NavigateToMembershipUpdate)
}
}
},
onFailure = {
Timber.e(it, "Error while getting membership status")
commands.emit(Command.NavigateToMembershipUpdate)
}
)
}
}
// fun onAddMoreSpacesClicked() {
// viewModelScope.launch {
// getMembership.async(GetMembershipStatus.Params(noCache = false)).fold(
// onSuccess = { membership ->
// if (membership != null) {
// val activeTier = TierId(membership.tier)
// if (activeTier.isPossibleToUpgrade(reason = MembershipUpgradeReason.NumberOfSharedSpaces)) {
// commands.emit(Command.NavigateToMembership)
// } else {
// commands.emit(Command.NavigateToMembershipUpdate)
// }
// }
// },
// onFailure = {
// Timber.e(it, "Error while getting membership status")
// commands.emit(Command.NavigateToMembershipUpdate)
// }
// )
// }
// }
fun onSpaceImagePicked(path: String) {
fun proceedWithSettingSpaceImage(path: String) {
Timber.d("onSpaceImageClicked: $path")
viewModelScope.launch {
uploadFile.async(
@ -385,8 +402,8 @@ class SpaceSettingsViewModel(
SetSpaceDetails.Params(
space = params.space,
details = mapOf(
Relations.ICON_OPTION to null,
Relations.ICON_IMAGE to file.id,
Relations.ICON_OPTION to null,
Relations.ICON_EMOJI to null
)
)
@ -406,12 +423,15 @@ class SpaceSettingsViewModel(
val createdBy: Id?,
val network: Id?,
val name: String,
val description: String,
val icon: SpaceIconView,
val isDeletable: Boolean = false,
val spaceType: SpaceType,
val permissions: SpaceMemberPermissions,
val shareLimitReached: ShareLimitsState,
val requests: Int = 0
val requests: Int = 0,
val isEditEnabled: Boolean,
val isUserOwner: Boolean,
val permissions: SpaceMemberPermissions,
)
data class ShareLimitsState(

View file

@ -2,7 +2,8 @@ package com.anytypeio.anytype.presentation.spaces
sealed class UiEvent {
data object OnBackPressed : UiEvent()
data class OnSavedClicked(val name: String, val description: String) : UiEvent()
data class OnSaveDescriptionClicked(val description: String) : UiEvent()
data class OnSaveTitleClicked(val title: String) : UiEvent()
data object OnDeleteSpaceClicked : UiEvent()
data object OnFileStorageClick : UiEvent()
data object OnPersonalizationClicked : UiEvent()

View file

@ -1,6 +1,7 @@
package com.anytypeio.anytype.presentation.spaces
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
import com.anytypeio.anytype.presentation.objects.ObjectIcon
sealed class UiSpaceSettingsState {
@ -8,13 +9,16 @@ sealed class UiSpaceSettingsState {
data class SpaceSettings(
val items: List<UiSpaceSettingsItem>,
val isEditEnabled: Boolean
) : UiSpaceSettingsState()
) : UiSpaceSettingsState() {
val name: String = items.filterIsInstance<UiSpaceSettingsItem.Name>()
.firstOrNull()?.name ?: EMPTY_STRING_VALUE
val description = items.filterIsInstance<UiSpaceSettingsItem.Description>()
.firstOrNull()?.description ?: EMPTY_STRING_VALUE
}
data class SpaceSettingsError(val message: String) : UiSpaceSettingsState()
}
sealed class UiSpaceSettingsItem {
sealed class Section : UiSpaceSettingsItem() {
data object Collaboration : Section()
data object ContentModel : Section()
@ -36,5 +40,4 @@ sealed class UiSpaceSettingsItem {
data class RemoteStorage(val size: Int) : UiSpaceSettingsItem()
data object SpaceInfo : UiSpaceSettingsItem()
data object DeleteSpace : UiSpaceSettingsItem()
}