diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectWrapper.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectWrapper.kt
index 1946cedfdc..e4dc73c82a 100644
--- a/core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectWrapper.kt
+++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/ObjectWrapper.kt
@@ -271,6 +271,7 @@ sealed class ObjectWrapper {
val id: Id by default
val name: String? by default
+ val description: String? = getSingleValue(Relations.DESCRIPTION)
val iconImage: String? get() = getSingleValue(Relations.ICON_IMAGE)
val iconOption: Double? by default
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/foundation/System.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/foundation/System.kt
index 434526c409..52781ab3e1 100644
--- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/foundation/System.kt
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/foundation/System.kt
@@ -20,11 +20,12 @@ import com.anytypeio.anytype.core_ui.views.Title1
@Composable
fun Section(
+ modifier: Modifier = Modifier,
title: String,
color: Color = colorResource(id = R.color.text_secondary),
textPaddingStart: Dp = 20.dp
) {
- Box(modifier = Modifier
+ Box(modifier = modifier
.height(52.dp)
.fillMaxWidth()) {
Text(
diff --git a/core-ui/src/main/res/drawable/ic_add_member_32.xml b/core-ui/src/main/res/drawable/ic_add_member_32.xml
new file mode 100644
index 0000000000..ca352df216
--- /dev/null
+++ b/core-ui/src/main/res/drawable/ic_add_member_32.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/core-ui/src/main/res/drawable/ic_disclosure_8_24.xml b/core-ui/src/main/res/drawable/ic_disclosure_8_24.xml
new file mode 100644
index 0000000000..b5636b7cf4
--- /dev/null
+++ b/core-ui/src/main/res/drawable/ic_disclosure_8_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/core-ui/src/main/res/drawable/ic_members_24.xml b/core-ui/src/main/res/drawable/ic_members_24.xml
new file mode 100644
index 0000000000..50048064d2
--- /dev/null
+++ b/core-ui/src/main/res/drawable/ic_members_24.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/core-ui/src/main/res/drawable/ic_object_types_24.xml b/core-ui/src/main/res/drawable/ic_object_types_24.xml
new file mode 100644
index 0000000000..3bbcab48fd
--- /dev/null
+++ b/core-ui/src/main/res/drawable/ic_object_types_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/core-ui/src/main/res/drawable/ic_qr_code_32.xml b/core-ui/src/main/res/drawable/ic_qr_code_32.xml
new file mode 100644
index 0000000000..901cdfb20d
--- /dev/null
+++ b/core-ui/src/main/res/drawable/ic_qr_code_32.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/core-ui/src/main/res/drawable/ic_remote_storage_24.xml b/core-ui/src/main/res/drawable/ic_remote_storage_24.xml
new file mode 100644
index 0000000000..99daee6ceb
--- /dev/null
+++ b/core-ui/src/main/res/drawable/ic_remote_storage_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/feature-ui-settings/build.gradle b/feature-ui-settings/build.gradle
index 5239a22449..eac608f655 100644
--- a/feature-ui-settings/build.gradle
+++ b/feature-ui-settings/build.gradle
@@ -32,6 +32,7 @@ dependencies {
implementation libs.appcompat
implementation libs.compose
+ implementation libs.composeMaterial3
implementation libs.composeFoundation
implementation libs.composeMaterial
implementation libs.composeToolingPreview
diff --git a/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Icon.kt b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Icon.kt
new file mode 100644
index 0000000000..9e7f246c9d
--- /dev/null
+++ b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Icon.kt
@@ -0,0 +1,134 @@
+package com.anytypeio.anytype.ui_settings.space.new_settings
+
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Divider
+import androidx.compose.material.DropdownMenu
+import androidx.compose.material.DropdownMenuItem
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.dp
+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.presentation.spaces.SpaceIconView
+import com.anytypeio.anytype.presentation.spaces.UiEvent
+import com.anytypeio.anytype.ui_settings.R
+import timber.log.Timber
+
+@Composable
+fun NewSpaceIcon(
+ icon: SpaceIconView,
+ modifier: Modifier = Modifier,
+ uiEvent: (UiEvent) -> Unit,
+ isEditEnabled: Boolean
+) {
+ val context = LocalContext.current
+ val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.PickVisualMedia(),
+ onResult = { uri ->
+ if (uri != null) {
+ uiEvent(UiEvent.OnSpaceImagePicked(uri.toString()))
+ } else {
+ Timber.w("Uri was null after picking image")
+ }
+ }
+ )
+ val isSpaceIconMenuExpanded = remember {
+ mutableStateOf(false)
+ }
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ SpaceIconView(
+ modifier = Modifier.size(112.dp),
+ icon = icon,
+ onSpaceIconClick = {
+ if (isEditEnabled) {
+ isSpaceIconMenuExpanded.value = !isSpaceIconMenuExpanded.value
+ }
+ }
+ )
+ Text(
+ modifier = Modifier
+ .padding(top = 8.dp)
+ .noRippleThrottledClickable {
+ if (isEditEnabled) {
+ isSpaceIconMenuExpanded.value = !isSpaceIconMenuExpanded.value
+ }
+ },
+ text = stringResource(R.string.space_settings_icon_title),
+ style = Caption1Medium
+ )
+ MaterialTheme(
+ shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(10.dp))
+ ) {
+ DropdownMenu(
+ modifier = Modifier
+ .background(
+ shape = RoundedCornerShape(10.dp),
+ color = colorResource(id = R.color.background_secondary)
+ ),
+ expanded = isSpaceIconMenuExpanded.value,
+ offset = DpOffset(x = 0.dp, y = 6.dp),
+ onDismissRequest = {
+ isSpaceIconMenuExpanded.value = false
+ }
+ ) {
+ if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable(context)) {
+ Divider(
+ thickness = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ DropdownMenuItem(
+ onClick = {
+ singlePhotoPickerLauncher.launch(
+ PickVisualMediaRequest(
+ ActivityResultContracts.PickVisualMedia.ImageOnly
+ )
+ )
+ isSpaceIconMenuExpanded.value = false
+ },
+ ) {
+ Text(
+ text = stringResource(R.string.space_settings_apply_upload_image),
+ style = BodyRegular,
+ color = colorResource(id = R.color.text_primary)
+ )
+ }
+ }
+ if (icon is SpaceIconView.Image) {
+ DropdownMenuItem(
+ onClick = {
+ uiEvent(UiEvent.IconMenu.OnRemoveIconClicked)
+ isSpaceIconMenuExpanded.value = false
+ },
+ ) {
+ Text(
+ text = stringResource(R.string.remove_image),
+ style = BodyRegular,
+ color = colorResource(id = R.color.text_primary)
+ )
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Items.kt b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Items.kt
new file mode 100644
index 0000000000..a332c1a501
--- /dev/null
+++ b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Items.kt
@@ -0,0 +1,499 @@
+package com.anytypeio.anytype.ui_settings.space.new_settings
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.TextFieldDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.anytypeio.anytype.core_ui.extensions.light
+import com.anytypeio.anytype.core_ui.foundation.Section
+import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
+import com.anytypeio.anytype.core_ui.views.BodyBold
+import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
+import com.anytypeio.anytype.core_ui.views.BodyRegular
+import com.anytypeio.anytype.core_ui.views.Caption1Regular
+import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
+import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
+import com.anytypeio.anytype.presentation.objects.ObjectIcon
+import com.anytypeio.anytype.presentation.spaces.UiEvent
+import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsItem
+import com.anytypeio.anytype.ui_settings.R
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.flow.filter
+
+@Composable
+fun MembersItem(
+ modifier: Modifier = Modifier,
+ item: UiSpaceSettingsItem.Members
+) {
+ BaseButton(
+ modifier = modifier,
+ title = stringResource(id = R.string.space_settings_members_button_members),
+ icon = R.drawable.ic_members_24,
+ count = item.count
+ )
+}
+
+@Composable
+fun ObjectTypesItem(
+ modifier: Modifier = Modifier
+) {
+ BaseButton(
+ modifier = modifier,
+ title = stringResource(id = R.string.space_settings_types_button),
+ icon = R.drawable.ic_object_types_24,
+ )
+}
+
+@Composable
+fun DefaultTypeItem(
+ modifier: Modifier = Modifier,
+ name: String,
+ icon: ObjectIcon
+) {
+ Row(
+ modifier = modifier
+ .border(
+ shape = RoundedCornerShape(16.dp),
+ width = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ .padding(vertical = 20.dp)
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(id = R.string.space_settings_default_type_button),
+ style = PreviewTitle1Regular,
+ color = colorResource(id = R.color.text_primary),
+ )
+ ListWidgetObjectIcon(
+ modifier = Modifier,
+ iconSize = 20.dp,
+ icon = icon
+ )
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ text = name.take(10),
+ style = PreviewTitle1Regular,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ color = colorResource(id = R.color.text_primary),
+ )
+ Image(
+ painter = painterResource(id = R.drawable.ic_disclosure_8_24),
+ contentDescription = "Members icon",
+ modifier = Modifier.size(24.dp)
+ )
+ }
+}
+
+@Composable
+fun WallpaperItem(
+ modifier: Modifier = Modifier,
+ item: UiSpaceSettingsItem.Wallpapers
+) {
+ Row(
+ modifier = modifier
+ .border(
+ shape = RoundedCornerShape(16.dp),
+ width = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ .padding(vertical = 20.dp)
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(id = R.string.space_settings_wallpaper_button),
+ style = PreviewTitle1Regular,
+ color = colorResource(id = R.color.text_primary),
+ )
+ Box(
+ modifier = Modifier
+ .size(20.dp)
+ .background(
+ color = light(item.color),
+ shape = RoundedCornerShape(4.dp)
+ )
+ .padding(horizontal = 6.dp),
+ )
+ Image(
+ painter = painterResource(id = R.drawable.ic_disclosure_8_24),
+ contentDescription = "Members icon",
+ modifier = Modifier.size(24.dp)
+ )
+ }
+}
+
+@Composable
+fun SpaceInfoItem(
+ modifier: Modifier = Modifier
+) {
+ BaseButton(
+ modifier = modifier,
+ title = stringResource(id = R.string.space_settings_space_info_button),
+ )
+}
+
+@Composable
+fun DeleteSpaceItem(
+ modifier: Modifier = Modifier
+) {
+ BaseButton(
+ modifier = modifier,
+ title = stringResource(id = R.string.space_settings_delete_space_button),
+ textColor = R.color.palette_dark_red
+ )
+}
+
+@Composable
+fun BaseButton(
+ modifier: Modifier = Modifier,
+ icon: Int? = null,
+ title: String,
+ count: Int? = null,
+ textColor: Int = R.color.text_primary,
+) {
+ Row(
+ modifier = modifier
+ .border(
+ shape = RoundedCornerShape(16.dp),
+ width = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ .padding(vertical = 20.dp)
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ if (icon != null) {
+ Image(
+ painter = painterResource(id = icon),
+ contentDescription = "Members icon",
+ modifier = Modifier.size(24.dp)
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ }
+ Text(
+ modifier = Modifier.weight(1f),
+ text = title,
+ style = PreviewTitle1Regular,
+ color = colorResource(id = textColor),
+ )
+ if (count != null) {
+ Text(
+ modifier = Modifier
+ .wrapContentSize()
+ .background(
+ color = colorResource(id = R.color.transparent_active),
+ shape = CircleShape
+ )
+ .padding(horizontal = 6.dp),
+ text = "$count",
+ textAlign = TextAlign.Center,
+ style = Caption1Regular,
+ color = colorResource(id = R.color.text_white),
+ )
+ }
+ Image(
+ painter = painterResource(id = R.drawable.ic_disclosure_8_24),
+ contentDescription = "Members icon",
+ modifier = Modifier.size(24.dp)
+ )
+ }
+}
+
+@OptIn(FlowPreview::class)
+@Composable
+fun NewSpaceNameBlock(
+ modifier: Modifier = Modifier,
+ name: String,
+ onNameSet: (String) -> Unit,
+ isEditEnabled: Boolean
+) {
+
+ val nameValue = remember { mutableStateOf(name) }
+ val focusManager = LocalFocusManager.current
+
+ LaunchedEffect(nameValue.value) {
+ snapshotFlow { nameValue.value }
+ .debounce(300L)
+ .dropWhile { input -> input == name }
+ .distinctUntilChanged()
+ .filter { it.isNotEmpty() }
+ .collect { query ->
+ onNameSet(query)
+ }
+ }
+
+ Column(modifier = modifier) {
+ Text(
+ text = stringResource(id = R.string.space_name),
+ style = BodyCalloutRegular.copy(color = colorResource(id = R.color.text_primary)),
+ color = colorResource(id = R.color.text_secondary)
+ )
+ NewSettingsTextField(
+ value = nameValue.value,
+ textStyle = BodyBold.copy(color = colorResource(id = R.color.text_primary)),
+ onValueChange = {
+ nameValue.value = it
+ },
+ keyboardActions = KeyboardActions(
+ onDone = {
+ focusManager.clearFocus()
+ }
+ ),
+ placeholderText = stringResource(id = R.string.space_settings_space_name_hint),
+ isEditEnabled = isEditEnabled
+ )
+ }
+}
+
+@OptIn(FlowPreview::class)
+@Composable
+fun NewSpaceDescriptionBlock(
+ modifier: Modifier = Modifier,
+ description: String,
+ onDescriptionSet: (String) -> Unit,
+ isEditEnabled: Boolean
+) {
+
+ val descriptionValue = remember { mutableStateOf(description) }
+ val focusManager = LocalFocusManager.current
+
+ LaunchedEffect(descriptionValue.value) {
+ snapshotFlow { descriptionValue.value }
+ .debounce(300L)
+ .dropWhile { input -> input == description }
+ .distinctUntilChanged()
+ .filter { it.isNotEmpty() }
+ .collect { query ->
+ onDescriptionSet(query)
+ }
+ }
+
+ Column(modifier = modifier) {
+ Text(
+ text = stringResource(id = R.string.space_settings_space_description_hint),
+ style = BodyCalloutRegular,
+ color = colorResource(id = R.color.text_secondary)
+ )
+ NewSettingsTextField(
+ textStyle = BodyRegular.copy(color = colorResource(id = R.color.text_primary)),
+ value = descriptionValue.value,
+ onValueChange = {
+ descriptionValue.value = it
+ },
+ keyboardActions = KeyboardActions(
+ onDone = {
+ focusManager.clearFocus()
+ }
+ ),
+ placeholderText = stringResource(id = R.string.space_settings_space_description_hint),
+ isEditEnabled = isEditEnabled
+ )
+ }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+fun NewSettingsTextField(
+ value: String,
+ onValueChange: (String) -> Unit,
+ visualTransformation: VisualTransformation = VisualTransformation.None,
+ keyboardActions: KeyboardActions = KeyboardActions.Default,
+ textStyle: TextStyle = BodyBold,
+ placeholderText: String,
+ isEditEnabled: Boolean
+) {
+ BasicTextField(
+ value = value,
+ modifier = Modifier
+ .padding(top = 4.dp)
+ .fillMaxWidth(),
+ onValueChange = onValueChange,
+ enabled = isEditEnabled,
+ readOnly = !isEditEnabled,
+ textStyle = textStyle,
+ cursorBrush = SolidColor(colorResource(id = R.color.orange)),
+ visualTransformation = visualTransformation,
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Done
+ ),
+ keyboardActions = keyboardActions,
+ interactionSource = remember { MutableInteractionSource() },
+ decorationBox = @Composable { innerTextField ->
+ TextFieldDefaults.OutlinedTextFieldDecorationBox(
+ value = value,
+ visualTransformation = visualTransformation,
+ innerTextField = innerTextField,
+ label = null,
+ leadingIcon = null,
+ trailingIcon = null,
+ singleLine = true,
+ enabled = true,
+ isError = false,
+ placeholder = {
+ androidx.compose.material.Text(
+ style = textStyle,
+ text = placeholderText,
+ color = colorResource(id = R.color.text_tertiary)
+ )
+ },
+ interactionSource = remember { MutableInteractionSource() },
+ colors = TextFieldDefaults.outlinedTextFieldColors(
+ textColor = colorResource(id = R.color.text_primary),
+ backgroundColor = Color.Transparent,
+ disabledBorderColor = Color.Transparent,
+ errorBorderColor = Color.Transparent,
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent,
+ placeholderColor = colorResource(id = R.color.text_tertiary),
+ cursorColor = colorResource(id = R.color.orange)
+ ),
+ contentPadding = PaddingValues(),
+ border = {}
+ )
+ }
+ )
+}
+
+@Composable
+fun MultiplayerButtons(
+ modifier: Modifier = Modifier,
+ uiEvent: (UiEvent) -> Unit
+) {
+ Row(
+ modifier = modifier,
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Column(
+ modifier = Modifier
+ .noRippleThrottledClickable {
+ uiEvent(UiEvent.OnInviteClicked)
+ }
+ .weight(1f)
+ .border(
+ shape = RoundedCornerShape(16.dp),
+ width = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ .padding(vertical = 14.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Image(
+ modifier = Modifier.size(32.dp),
+ painter = painterResource(id = R.drawable.ic_add_member_32),
+ contentDescription = "Invite new member icon"
+ )
+ Text(
+ modifier = Modifier.wrapContentSize(),
+ text = stringResource(id = R.string.space_settings_invite),
+ style = Caption1Regular,
+ color = colorResource(id = R.color.text_primary)
+ )
+ }
+
+ Column(
+ modifier = Modifier
+ .noRippleThrottledClickable {
+ uiEvent(UiEvent.OnQrCodeClicked)
+ }
+ .weight(1f)
+ .border(
+ shape = RoundedCornerShape(16.dp),
+ width = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ .padding(vertical = 14.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Image(
+ modifier = Modifier.size(32.dp),
+ painter = painterResource(id = R.drawable.ic_qr_code_32),
+ contentDescription = "Share QR code icon"
+ )
+ Text(
+ modifier = Modifier.wrapContentSize(),
+ text = stringResource(id = R.string.space_settings_qrcode),
+ style = Caption1Regular,
+ color = colorResource(id = R.color.text_primary)
+ )
+ }
+ }
+}
+
+@Composable
+fun SpaceSettingsSection(
+ modifier: Modifier = Modifier,
+ item: UiSpaceSettingsItem.Section
+) {
+ val text = when (item) {
+ UiSpaceSettingsItem.Section.Collaboration ->
+ stringResource(id = R.string.space_settings_section_collaboration)
+
+ UiSpaceSettingsItem.Section.ContentModel ->
+ stringResource(id = R.string.space_settings_section_content_model)
+
+ UiSpaceSettingsItem.Section.DataManagement ->
+ stringResource(id = R.string.space_settings_section_data_management)
+
+ UiSpaceSettingsItem.Section.Misc ->
+ stringResource(id = R.string.space_settings_section_misc)
+
+ UiSpaceSettingsItem.Section.Preferences ->
+ stringResource(id = R.string.space_settings_section_preferences)
+ }
+ Section(
+ modifier = modifier,
+ title = text,
+ textPaddingStart = 0.dp
+ )
+}
\ No newline at end of file
diff --git a/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/NewSettings.kt b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/NewSettings.kt
new file mode 100644
index 0000000000..14724a6f49
--- /dev/null
+++ b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/NewSettings.kt
@@ -0,0 +1,305 @@
+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
+import androidx.compose.foundation.layout.navigationBars
+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.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+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.noRippleClickable
+import com.anytypeio.anytype.core_ui.views.PreviewTitle1Medium
+import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
+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
+
+@Composable
+fun SpaceSettingsContainer(
+ uiState: UiSpaceSettingsState,
+ uiEvent: (UiEvent) -> Unit
+) {
+ if (uiState is UiSpaceSettingsState.SpaceSettings) {
+ NewSpaceSettingsScreen(
+ uiState = uiState,
+ uiEvent = uiEvent
+ )
+ }
+}
+
+@Composable
+fun NewSpaceSettingsScreen(
+ uiState: UiSpaceSettingsState.SpaceSettings,
+ uiEvent: (UiEvent) -> Unit
+) {
+
+ // Get the initial values from your uiState items.
+ val initialName = uiState.items.filterIsInstance()
+ .firstOrNull()?.name ?: ""
+ val initialDescription = uiState.items.filterIsInstance()
+ .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
+
+ Scaffold(
+ modifier = Modifier.fillMaxWidth(),
+ containerColor = colorResource(id = R.color.background_primary),
+ topBar = {
+ Box(
+ modifier = if (Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
+ Modifier
+ .windowInsetsPadding(WindowInsets.statusBars)
+ .fillMaxWidth()
+ .height(48.dp)
+ else
+ Modifier
+ .fillMaxWidth()
+ .height(48.dp)
+ ) {
+ Image(
+ painter = painterResource(R.drawable.ic_home_top_toolbar_back),
+ contentDescription = "Back button",
+ contentScale = ContentScale.Inside,
+ modifier = Modifier
+ .padding(start = 4.dp)
+ .size(48.dp)
+ .align(Alignment.CenterStart)
+ .noRippleClickable {
+ 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 ->
+ 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)
+ val lazyListState = rememberLazyListState()
+
+ LazyColumn(
+ modifier = contentModifier
+ .padding(horizontal = 16.dp),
+ state = lazyListState,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ uiState.items.forEach { item ->
+ when (item) {
+ is UiSpaceSettingsItem.Icon -> {
+ item {
+ NewSpaceIcon(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem(),
+ icon = item.icon,
+ isEditEnabled = uiState.isEditEnabled,
+ uiEvent = uiEvent
+ )
+ }
+ }
+
+ is UiSpaceSettingsItem.Name -> {
+ item {
+ NewSpaceNameBlock(
+ modifier = Modifier
+ .fillMaxWidth()
+ .border(
+ shape = RoundedCornerShape(16.dp),
+ width = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ .padding(vertical = 12.dp, horizontal = 16.dp)
+ .animateItem(),
+ name = nameInput,
+ onNameSet = { newName ->
+ nameInput = newName
+ },
+ isEditEnabled = uiState.isEditEnabled
+ )
+ }
+ }
+
+ is UiSpaceSettingsItem.Description -> {
+ item {
+ NewSpaceDescriptionBlock(
+ modifier = Modifier
+ .fillMaxWidth()
+ .border(
+ shape = RoundedCornerShape(16.dp),
+ width = 0.5.dp,
+ color = colorResource(id = R.color.shape_primary)
+ )
+ .padding(vertical = 12.dp, horizontal = 16.dp)
+ .animateItem(),
+ isEditEnabled = uiState.isEditEnabled,
+ description = descriptionInput,
+ onDescriptionSet = { newDescription ->
+ descriptionInput = newDescription
+ }
+ )
+ }
+ }
+
+ UiSpaceSettingsItem.Multiplayer -> {
+ item {
+ MultiplayerButtons(
+ modifier = Modifier
+ .fillMaxWidth(),
+ uiEvent = uiEvent
+ )
+ }
+ }
+
+ is UiSpaceSettingsItem.Chat -> TODO()
+ is UiSpaceSettingsItem.DefaultObjectType -> {
+ item {
+ DefaultTypeItem(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem(),
+ name = item.name,
+ icon = item.icon
+ )
+ }
+ }
+
+ UiSpaceSettingsItem.DeleteSpace -> {
+ item {
+ DeleteSpaceItem(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem()
+ )
+ }
+ }
+
+ is UiSpaceSettingsItem.Members -> {
+ item {
+ MembersItem(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem(),
+ item = item
+ )
+ }
+ }
+
+ UiSpaceSettingsItem.ObjectTypes -> {
+ item {
+ ObjectTypesItem(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem()
+ )
+ }
+ }
+
+ is UiSpaceSettingsItem.RemoteStorage -> TODO()
+ is UiSpaceSettingsItem.Section -> {
+ item {
+ SpaceSettingsSection(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem(),
+ item = item
+ )
+ }
+ }
+
+ UiSpaceSettingsItem.SpaceInfo -> {
+ item {
+ SpaceInfoItem(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem()
+ )
+ }
+ }
+
+ is UiSpaceSettingsItem.Wallpapers -> {
+ item {
+ WallpaperItem(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItem(),
+ item = item
+ )
+ }
+ }
+
+ is UiSpaceSettingsItem.Spacer -> {
+ item {
+ Spacer(modifier = Modifier.height(item.height.dp))
+ }
+ }
+ }
+
+ }
+ }
+ }
+ )
+
+}
\ No newline at end of file
diff --git a/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Previews.kt b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Previews.kt
new file mode 100644
index 0000000000..0a62607242
--- /dev/null
+++ b/feature-ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/space/new_settings/Previews.kt
@@ -0,0 +1,51 @@
+package com.anytypeio.anytype.ui_settings.space.new_settings
+
+import androidx.compose.runtime.Composable
+import com.anytypeio.anytype.core_models.ThemeColor
+import com.anytypeio.anytype.core_ui.common.DefaultPreviews
+import com.anytypeio.anytype.presentation.objects.ObjectIcon
+import com.anytypeio.anytype.presentation.spaces.SpaceIconView
+import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsItem
+import com.anytypeio.anytype.presentation.spaces.UiSpaceSettingsState
+
+@Composable
+@DefaultPreviews
+fun NewSpaceSettingsScreenPreview() {
+ NewSpaceSettingsScreen(
+ uiState = UiSpaceSettingsState.SpaceSettings(
+ items = listOf(
+ UiSpaceSettingsItem.Spacer(height = 8),
+ UiSpaceSettingsItem.Icon(SpaceIconView.Placeholder()),
+ UiSpaceSettingsItem.Spacer(height = 16),
+ UiSpaceSettingsItem.Name("Dream team"),
+ UiSpaceSettingsItem.Spacer(height = 12),
+ UiSpaceSettingsItem.Description("This is a dream team space"),
+ UiSpaceSettingsItem.Spacer(height = 12),
+ UiSpaceSettingsItem.Multiplayer,
+ UiSpaceSettingsItem.Spacer(height = 8),
+ UiSpaceSettingsItem.Section.Collaboration,
+ UiSpaceSettingsItem.Members(5),
+ UiSpaceSettingsItem.Section.ContentModel,
+ UiSpaceSettingsItem.ObjectTypes,
+ UiSpaceSettingsItem.Section.Preferences,
+ UiSpaceSettingsItem.DefaultObjectType(
+ name = "Taskwithveryverlylongname",
+ icon = ObjectIcon.Empty.ObjectType
+ ),
+ UiSpaceSettingsItem.Spacer(height = 8),
+ UiSpaceSettingsItem.Wallpapers(color = ThemeColor.TEAL),
+ UiSpaceSettingsItem.Spacer(height = 8),
+ UiSpaceSettingsItem.Section.DataManagement,
+ UiSpaceSettingsItem.Spacer(height = 8),
+ UiSpaceSettingsItem.Section.Misc,
+ UiSpaceSettingsItem.SpaceInfo,
+ UiSpaceSettingsItem.Spacer(height = 8),
+ UiSpaceSettingsItem.DeleteSpace,
+ UiSpaceSettingsItem.Spacer(height = 32),
+
+ ),
+ isEditEnabled = true
+ ),
+ uiEvent = {},
+ )
+}
\ No newline at end of file
diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml
index 7627c7cc0f..09ed77968f 100644
--- a/localization/src/main/res/values/strings.xml
+++ b/localization/src/main/res/values/strings.xml
@@ -1943,4 +1943,23 @@ Please provide specific details of your needs here.
Add to the current type
Remove from the object
+ Name
+ Description
+ Collaboration
+ Content Model
+ Preferences
+ Data Management
+ Misc
+ Invite
+ QR Code
+ Members
+ Object Types
+ Default Object Type
+ Wallpaper
+ Remote Storage
+ Space Information
+ Delete Space
+ Edit picture
+ Save
+
\ No newline at end of file
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/UiEvent.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/UiEvent.kt
new file mode 100644
index 0000000000..531e0614f9
--- /dev/null
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/UiEvent.kt
@@ -0,0 +1,18 @@
+package com.anytypeio.anytype.presentation.spaces
+
+sealed class UiEvent {
+ data object OnBackPressed : UiEvent()
+ data class OnSavedClicked(val name: String, val description: String) : UiEvent()
+ data object OnDeleteSpaceClicked : UiEvent()
+ data object OnFileStorageClick : UiEvent()
+ data object OnPersonalizationClicked : UiEvent()
+ data object OnSpaceIdClicked : UiEvent()
+ data class OnSpaceImagePicked(val uri: String) : UiEvent()
+ data object OnInviteClicked : UiEvent()
+ data object OnQrCodeClicked : UiEvent()
+
+ sealed class IconMenu : UiEvent() {
+ data object OnRemoveIconClicked : IconMenu()
+ }
+
+}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/UiState.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/UiState.kt
new file mode 100644
index 0000000000..b37acab578
--- /dev/null
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/spaces/UiState.kt
@@ -0,0 +1,40 @@
+package com.anytypeio.anytype.presentation.spaces
+
+import com.anytypeio.anytype.core_models.ThemeColor
+import com.anytypeio.anytype.presentation.objects.ObjectIcon
+
+sealed class UiSpaceSettingsState {
+ data object Initial : UiSpaceSettingsState()
+ data class SpaceSettings(
+ val items: List,
+ val isEditEnabled: Boolean
+ ) : UiSpaceSettingsState()
+
+ data class SpaceSettingsError(val message: String) : UiSpaceSettingsState()
+}
+
+sealed class UiSpaceSettingsItem {
+
+ sealed class Section : UiSpaceSettingsItem() {
+ data object Collaboration : Section()
+ data object ContentModel : Section()
+ data object Preferences : Section()
+ data object DataManagement : Section()
+ data object Misc : Section()
+ }
+
+ data class Spacer(val height: Int) : UiSpaceSettingsItem()
+ data class Icon(val icon: SpaceIconView) : UiSpaceSettingsItem()
+ data class Name(val name: String) : UiSpaceSettingsItem()
+ data class Description(val description: String) : UiSpaceSettingsItem()
+ data object Multiplayer : UiSpaceSettingsItem()
+ data class Members(val count: Int) : UiSpaceSettingsItem()
+ data class Chat(val isOn: Boolean) : UiSpaceSettingsItem()
+ data object ObjectTypes : UiSpaceSettingsItem()
+ data class DefaultObjectType(val name: String, val icon: ObjectIcon) : UiSpaceSettingsItem()
+ data class Wallpapers(val color: ThemeColor) : UiSpaceSettingsItem()
+ data class RemoteStorage(val size: Int) : UiSpaceSettingsItem()
+ data object SpaceInfo : UiSpaceSettingsItem()
+ data object DeleteSpace : UiSpaceSettingsItem()
+
+}
\ No newline at end of file