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

@ -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
}
)
}
}
)
}