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

DROID-3435 Primitives | Ui fixes (#2152)

This commit is contained in:
Konstantin Ivanov 2025-03-12 13:01:22 +01:00 committed by GitHub
parent a65f78c8d4
commit 58f64ffd61
Signed by: github
GPG key ID: B5690EEEBB952194
19 changed files with 434 additions and 118 deletions

View file

@ -28,7 +28,12 @@ import com.anytypeio.anytype.feature_properties.EditTypePropertiesViewModel
import com.anytypeio.anytype.feature_properties.EditTypePropertiesViewModel.EditTypePropertiesCommand
import com.anytypeio.anytype.feature_properties.add.EditTypePropertiesVmParams
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesErrorState
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesEvent
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesEvent.OnPropertyFormatSelected
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesEvent.OnPropertyFormatsListDismiss
import com.anytypeio.anytype.feature_properties.add.ui.AddFieldScreen
import com.anytypeio.anytype.feature_properties.edit.UiPropertyFormatsListState
import com.anytypeio.anytype.feature_properties.edit.ui.PropertyFormatsListScreen
import javax.inject.Inject
class EditTypePropertiesFragment : BaseBottomSheetComposeFragment() {
@ -51,6 +56,7 @@ class EditTypePropertiesFragment : BaseBottomSheetComposeFragment() {
uiStateEditProperty = vm.uiPropertyEditState.collectAsStateWithLifecycle().value,
event = vm::onEvent
)
PropertyFormatsScreen()
ErrorScreen()
}
}
@ -100,6 +106,18 @@ class EditTypePropertiesFragment : BaseBottomSheetComposeFragment() {
}
}
@Composable
private fun PropertyFormatsScreen() {
val uiState = vm.uiPropertyFormatsListState.collectAsStateWithLifecycle().value
if (uiState is UiPropertyFormatsListState.Visible) {
PropertyFormatsListScreen(
uiState = uiState,
onDismissRequest = { vm.onEvent(OnPropertyFormatsListDismiss) },
onFormatClick = { vm.onEvent(OnPropertyFormatSelected(it)) }
)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupBottomSheetBehavior(DEFAULT_PADDING_TOP)

View file

@ -172,6 +172,7 @@ class ObjectTypeFragment : BaseComposeFragment() {
uiIconState = vm.uiIconState.collectAsStateWithLifecycle().value,
uiEditPropertyState = vm.uiEditPropertyScreen.collectAsStateWithLifecycle().value,
uiFieldLocalInfoState = vm.uiFieldLocalInfoState.collectAsStateWithLifecycle().value,
withDragger = false,
fieldEvent = vm::onFieldEvent
)
}

View file

@ -168,6 +168,9 @@ open class ObjectSetFragment :
private val topBackButton: View
get() = binding.topToolbar.root.findViewById(R.id.topBackButton)
private val topToolbar: ViewGroup
get() = binding.topToolbar.root
private val topToolbarTitle: TextView
get() = binding.topToolbar.root.findViewById(R.id.tvTopToolbarTitle)
@ -708,7 +711,9 @@ open class ObjectSetFragment :
is DataViewViewState.TypeSet.Default -> {
topToolbarThreeDotsButton.gone()
topToolbarStatusContainer.gone()
topToolbarTitle.gone()
topBackButton.gone()
topToolbar.gone()
initView.gone()
header.gone()
dataViewHeader.visible()
@ -727,6 +732,8 @@ open class ObjectSetFragment :
is DataViewViewState.TypeSet.NoItems -> {
topToolbarThreeDotsButton.gone()
topToolbarStatusContainer.gone()
topToolbarTitle.gone()
topToolbar.gone()
topBackButton.gone()
initView.gone()
header.gone()
@ -750,7 +757,9 @@ open class ObjectSetFragment :
is DataViewViewState.TypeSet.Error -> {
topToolbarThreeDotsButton.gone()
topToolbarStatusContainer.gone()
topToolbarTitle.gone()
topBackButton.gone()
topToolbar.gone()
initView.gone()
header.gone()
dataViewHeader.visible()

View file

@ -26,4 +26,9 @@ sealed class FieldEvent {
data class OnMove(val fromKey: String, val toKey: String) : DragEvent()
data object OnDragEnd : DragEvent()
}
sealed class EditProperty : FieldEvent() {
data class OnPropertyNameUpdate(val name: String) : EditProperty()
data object OnSaveButtonClicked : EditProperty()
}
}

View file

@ -44,7 +44,7 @@ sealed class UiFieldsListItem {
abstract val fieldTitle: String
abstract val format: RelationFormat
abstract val limitObjectTypes: List<UiPropertyLimitTypeItem>
abstract val canDelete: Boolean
abstract val isPossibleToUnlinkFromType: Boolean
abstract val isEditableField: Boolean
data class Draggable(
@ -53,7 +53,7 @@ sealed class UiFieldsListItem {
override val fieldTitle: String,
override val format: RelationFormat,
override val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList(),
override val canDelete: Boolean,
override val isPossibleToUnlinkFromType: Boolean,
override val isEditableField: Boolean
) : Item()
@ -63,7 +63,7 @@ sealed class UiFieldsListItem {
override val fieldTitle: String,
override val format: RelationFormat,
override val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList(),
override val canDelete: Boolean = false,
override val isPossibleToUnlinkFromType: Boolean = false,
override val isEditableField: Boolean
) : Item()
}

View file

@ -4,6 +4,7 @@ import android.os.Build
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -47,15 +48,14 @@ import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.common.ReorderHapticFeedback
import com.anytypeio.anytype.core_ui.common.ReorderHapticFeedbackType
import com.anytypeio.anytype.core_ui.common.bottomBorder
import com.anytypeio.anytype.core_ui.common.rememberReorderHapticFeedback
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyCalloutMedium
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.Caption1Medium
import com.anytypeio.anytype.core_ui.views.Relations1
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
@ -85,6 +85,7 @@ fun FieldsMainScreen(
uiIconState: UiIconState,
uiFieldLocalInfoState: UiLocalsFieldsInfoState,
uiEditPropertyState: UiEditPropertyState,
withDragger: Boolean = true,
fieldEvent: (FieldEvent) -> Unit
) {
@ -125,7 +126,8 @@ fun FieldsMainScreen(
TopBar(
modifier = Modifier.fillMaxWidth(),
uiTitleState = uiTitleState,
uiIconState = uiIconState
uiIconState = uiIconState,
withDragger = withDragger
)
},
content = { paddingValues ->
@ -141,7 +143,8 @@ fun FieldsMainScreen(
}
LazyColumn(
modifier = contentModifier,
state = lazyListState
state = lazyListState,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(
count = uiFieldsListState.items.size,
@ -224,10 +227,10 @@ fun FieldsMainScreen(
onDismissRequest = { fieldEvent(OnEditPropertyScreenDismiss) },
onFormatClick = {},
onLimitTypesClick = {},
onSaveButtonClicked = {},
onSaveButtonClicked = { fieldEvent(EditProperty.OnSaveButtonClicked) },
onCreateNewButtonClicked = {},
onPropertyNameUpdate = { },
onDeleteButtonClicked = { id -> fieldEvent(OnDeleteFromTypeClick(id)) },
onPropertyNameUpdate = { fieldEvent(EditProperty.OnPropertyNameUpdate(it)) },
onMenuUnlinkClick = { fieldEvent(OnDeleteFromTypeClick(it)) }
)
}
@ -259,15 +262,20 @@ private fun getContentType(item: UiFieldsListItem): String {
/** A common modifier for list items. **/
@Composable
fun LazyItemScope.commonItemModifier() = Modifier
.height(52.dp)
.height(48.dp)
.fillMaxWidth()
.padding(horizontal = 20.dp)
.bottomBorder()
.padding(horizontal = 16.dp)
.border(
width = 1.dp,
color = colorResource(id = R.color.shape_primary),
shape = RoundedCornerShape(12.dp)
)
.animateItem()
@Composable
private fun TopBar(
modifier: Modifier,
withDragger: Boolean = true,
uiTitleState: UiTitleState,
uiIconState: UiIconState,
) {
@ -283,11 +291,13 @@ private fun TopBar(
shape = RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
) {
Dragger(
modifier = Modifier
.padding(vertical = 6.dp)
.align(Alignment.CenterHorizontally)
)
if (withDragger) {
Dragger(
modifier = Modifier
.padding(vertical = 6.dp)
.align(Alignment.CenterHorizontally)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
@ -388,7 +398,7 @@ private fun LazyItemScope.SectionItem(
) {
Text(
modifier = Modifier
.padding(bottom = 7.dp, start = 20.dp)
.padding(bottom = 4.dp, start = 20.dp)
.align(Alignment.BottomStart),
text = title,
style = BodyCalloutMedium,
@ -406,7 +416,7 @@ private fun LazyItemScope.SectionItem(
) {
Image(
modifier = Modifier
.padding(bottom = 6.dp, end = 20.dp)
.padding(bottom = 2.dp, end = 20.dp)
.wrapContentSize()
.align(Alignment.BottomEnd),
painter = painterResource(R.drawable.ic_default_plus),
@ -459,7 +469,7 @@ private fun FieldItemLocal(
if (formatIcon != null) {
Image(
modifier = Modifier
.padding(end = 10.dp)
.padding(start = 14.dp, end = 8.dp)
.size(24.dp),
painter = painterResource(id = formatIcon),
contentDescription = "Relation format icon",
@ -472,7 +482,7 @@ private fun FieldItemLocal(
.weight(1.0f)
.padding(end = 16.dp),
text = item.fieldTitle,
style = BodyRegular,
style = Relations1,
color = colorResource(id = R.color.text_primary),
maxLines = 1,
overflow = TextOverflow.Ellipsis
@ -480,6 +490,7 @@ private fun FieldItemLocal(
Image(
modifier = Modifier
.padding(end = 14.dp)
.size(24.dp)
.noRippleThrottledClickable {
isMenuExpanded.value = true
@ -524,7 +535,7 @@ private fun LazyItemScope.FieldItemDraggable(
if (formatIcon != null) {
Image(
modifier = Modifier
.padding(end = 10.dp)
.padding(start = 14.dp, end = 8.dp)
.size(24.dp),
painter = painterResource(id = formatIcon),
contentDescription = "Relation format icon",
@ -541,7 +552,7 @@ private fun LazyItemScope.FieldItemDraggable(
},
onLongClick = {
// show your menu, only if NOT dragging
if (item.canDelete) {
if (item.isPossibleToUnlinkFromType) {
isMenuExpanded.value = true
}
}
@ -553,7 +564,7 @@ private fun LazyItemScope.FieldItemDraggable(
.fillMaxWidth()
.padding(end = 16.dp),
text = item.fieldTitle,
style = BodyRegular,
style = Relations1,
color = colorResource(id = R.color.text_primary),
maxLines = 1,
overflow = TextOverflow.Ellipsis
@ -562,6 +573,7 @@ private fun LazyItemScope.FieldItemDraggable(
Image(
modifier = Modifier
.padding(end = 14.dp)
.size(24.dp)
.draggableHandle(
onDragStarted = {
@ -668,7 +680,7 @@ fun PreviewTypeFieldsMainScreen() {
fieldKey = "key1",
fieldTitle = "Status",
format = RelationFormat.STATUS,
canDelete = true,
isPossibleToUnlinkFromType = true,
isEditableField = true
),
UiFieldsListItem.Item.Draggable(
@ -676,7 +688,7 @@ fun PreviewTypeFieldsMainScreen() {
fieldKey = "key2",
fieldTitle = "Very long field title, just to test how it looks",
format = RelationFormat.LONG_TEXT,
canDelete = true,
isPossibleToUnlinkFromType = true,
isEditableField = true
),
UiFieldsListItem.Section.SideBar(
@ -688,7 +700,7 @@ fun PreviewTypeFieldsMainScreen() {
fieldTitle = "Links",
format = RelationFormat.URL,
isEditableField = true,
canDelete = true
isPossibleToUnlinkFromType = true
),
UiFieldsListItem.Item.Draggable(
id = "id4",
@ -696,7 +708,7 @@ fun PreviewTypeFieldsMainScreen() {
fieldTitle = "Very long field title, just to test how it looks",
format = RelationFormat.DATE,
isEditableField = true,
canDelete = true
isPossibleToUnlinkFromType = true
),
UiFieldsListItem.Section.Hidden(),
UiFieldsListItem.Item.Draggable(
@ -705,7 +717,7 @@ fun PreviewTypeFieldsMainScreen() {
fieldTitle = "Hidden field",
format = RelationFormat.LONG_TEXT,
isEditableField = true,
canDelete = true
isPossibleToUnlinkFromType = true
),
UiFieldsListItem.Section.Local(),
UiFieldsListItem.Item.Local(

View file

@ -210,7 +210,7 @@ private suspend fun mapToUiFieldsDraggableListItem(
format = field.format,
limitObjectTypes = limitObjectTypes,
isEditableField = fieldParser.isFieldEditable(field),
canDelete = fieldParser.isFieldCanBeDeletedFromType(field)
isPossibleToUnlinkFromType = fieldParser.isFieldCanBeDeletedFromType(field)
)
}

View file

@ -50,6 +50,7 @@ import com.anytypeio.anytype.feature_object_type.ui.UiTitleState
import com.anytypeio.anytype.feature_object_type.ui.buildUiFieldsList
import com.anytypeio.anytype.feature_object_type.ui.toTemplateView
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState.Visible.View
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.extension.sendAnalyticsScreenObjectType
@ -113,7 +114,8 @@ class ObjectTypeViewModel(
//layout, fields and templates buttons
val uiFieldsButtonState = MutableStateFlow<UiFieldsButtonState>(UiFieldsButtonState.Hidden)
val uiLayoutButtonState = MutableStateFlow<UiLayoutButtonState>(UiLayoutButtonState.Hidden)
val uiTemplatesButtonState = MutableStateFlow<UiTemplatesButtonState>(UiTemplatesButtonState.Hidden)
val uiTemplatesButtonState =
MutableStateFlow<UiTemplatesButtonState>(UiTemplatesButtonState.Hidden)
//type layouts
val uiTypeLayoutsState = MutableStateFlow<UiLayoutTypeState>(Hidden)
@ -376,7 +378,8 @@ class ObjectTypeViewModel(
formatName = stringResourceProvider.getPropertiesFormatPrettyString(item.format),
formatIcon = item.format.simpleIcon(),
format = item.format,
limitObjectTypes = item.limitObjectTypes
limitObjectTypes = item.limitObjectTypes,
isPossibleToUnlinkFromType = item.isPossibleToUnlinkFromType
)
} else {
uiEditPropertyScreen.value = UiEditPropertyState.Visible.View(
@ -386,7 +389,8 @@ class ObjectTypeViewModel(
formatName = stringResourceProvider.getPropertiesFormatPrettyString(item.format),
formatIcon = item.format.simpleIcon(),
format = item.format,
limitObjectTypes = item.limitObjectTypes
limitObjectTypes = item.limitObjectTypes,
isPossibleToUnlinkFromType = item.isPossibleToUnlinkFromType
)
}
}
@ -696,6 +700,7 @@ class ObjectTypeViewModel(
currentList.add(toIndex, item)
uiFieldsListState.value = UiFieldsListState(items = currentList)
}
is FieldEvent.EditProperty -> proceedWithEditPropertyEvent(event)
}
}
@ -749,7 +754,44 @@ class ObjectTypeViewModel(
val newRecommendedFields = currentRecommendedFields + event.item.id
proceedWithSetRecommendedFields(newRecommendedFields)
}
}
}
private fun proceedWithEditPropertyEvent(event: FieldEvent.EditProperty) {
when (event) {
is FieldEvent.EditProperty.OnPropertyNameUpdate -> {
val state = uiEditPropertyScreen.value as? UiEditPropertyState.Visible ?: return
uiEditPropertyScreen.value = when (state) {
is UiEditPropertyState.Visible.Edit -> state.copy(name = event.name)
is UiEditPropertyState.Visible.New -> state.copy(name = event.name)
is View -> state
}
}
FieldEvent.EditProperty.OnSaveButtonClicked -> {
val state =
uiEditPropertyScreen.value as? UiEditPropertyState.Visible.Edit ?: return
viewModelScope.launch {
val params = SetObjectDetails.Params(
ctx = state.id,
details = mapOf(
Relations.NAME to state.name
)
)
setObjectDetails.async(params).fold(
onSuccess = {
Timber.d("Relation updated: $it")
uiEditPropertyScreen.value = UiEditPropertyState.Hidden
},
onFailure = { error ->
Timber.e(error, "Failed to update relation")
errorState.value = UiErrorState.Show(
reason = UiErrorState.Reason.Other(error.message ?: "")
)
}
)
}
}
}
}
//endregion

View file

@ -18,9 +18,13 @@ import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesEvent
import com.anytypeio.anytype.feature_properties.add.EditTypePropertiesVmParams
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesErrorState
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesItem
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesItem.*
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesState
import com.anytypeio.anytype.feature_properties.add.mapToStateItem
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState.Visible.*
import com.anytypeio.anytype.feature_properties.edit.UiPropertyFormatsListState
import com.anytypeio.anytype.feature_properties.edit.UiPropertyFormatsListState.*
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@ -57,6 +61,9 @@ class EditTypePropertiesViewModel(
val uiPropertyEditState =
MutableStateFlow<UiEditPropertyState>(UiEditPropertyState.Hidden)
val uiPropertyFormatsListState =
MutableStateFlow<UiPropertyFormatsListState>(UiPropertyFormatsListState.Hidden)
private val _commands = MutableSharedFlow<EditTypePropertiesCommand>()
val commands = _commands.asSharedFlow()
@ -184,10 +191,11 @@ class EditTypePropertiesViewModel(
//region Ui Events
fun onEvent(event: UiEditTypePropertiesEvent) {
Timber.d("UiEditTypePropertiesEvent: $event")
when (event) {
is UiEditTypePropertiesEvent.OnCreate -> {
val format = event.item.format
uiPropertyEditState.value = UiEditPropertyState.Visible.New(
uiPropertyEditState.value = New(
name = event.item.title,
formatName = stringResourceProvider.getPropertiesFormatPrettyString(format),
formatIcon = format.simpleIcon(),
@ -212,7 +220,7 @@ class EditTypePropertiesViewModel(
is UiEditTypePropertiesEvent.OnTypeClicked -> {
val format = event.item.format
uiPropertyEditState.value = UiEditPropertyState.Visible.New(
uiPropertyEditState.value = New(
name = "",
formatName = stringResourceProvider.getPropertiesFormatPrettyString(format),
formatIcon = format.simpleIcon(),
@ -235,9 +243,43 @@ class EditTypePropertiesViewModel(
is UiEditTypePropertiesEvent.OnPropertyNameUpdate -> {
val state = uiPropertyEditState.value as? UiEditPropertyState.Visible ?: return
uiPropertyEditState.value = when (state) {
is UiEditPropertyState.Visible.Edit -> state.copy(name = event.name)
is UiEditPropertyState.Visible.New -> state.copy(name = event.name)
is UiEditPropertyState.Visible.View -> state
is Edit -> state.copy(name = event.name)
is New -> state.copy(name = event.name)
is View -> state
}
}
UiEditTypePropertiesEvent.OnPropertyFormatClick -> {
uiPropertyFormatsListState.value = Visible(
items = UiEditTypePropertiesState.Companion.PROPERTIES_FORMATS.map { format ->
Format(
format = format,
prettyName = stringResourceProvider.getPropertiesFormatPrettyString(format)
)
}
)
}
UiEditTypePropertiesEvent.OnPropertyFormatsListDismiss -> {
uiPropertyFormatsListState.value = Hidden
}
is UiEditTypePropertiesEvent.OnPropertyFormatSelected -> {
uiPropertyFormatsListState.value = Hidden
val state = uiPropertyEditState.value as? UiEditPropertyState.Visible ?: return
uiPropertyEditState.value = when (state) {
is New -> {
val newFormat = event.format.format
state.copy(
formatName = stringResourceProvider.getPropertiesFormatPrettyString(
newFormat
),
formatIcon = newFormat.simpleIcon(),
format = newFormat,
)
}
else -> state
}
}
}
@ -246,21 +288,20 @@ class EditTypePropertiesViewModel(
//region Use Cases
private fun proceedWithUpdatingRelation() {
val state = uiPropertyEditState.value as? UiEditPropertyState.Visible.Edit ?: return
val state = uiPropertyEditState.value as? Edit ?: return
viewModelScope.launch {
val params = SetObjectDetails.Params(
ctx = state.id,
details = mapOf(
Relations.NAME to state.name,
Relations.RELATION_FORMAT to state.format
Relations.NAME to state.name
)
)
setObjectDetails.async(params).fold(
onSuccess = {
Timber.d("Relation updated: $it")
onSuccess = { payload ->
Timber.d("Property updated :[$payload]")
},
onFailure = { error ->
Timber.e(error, "Failed to update relation")
Timber.e(error, "Failed to update property")
_errorState.value = UiEditTypePropertiesErrorState.Show(
UiEditTypePropertiesErrorState.Reason.ErrorUpdatingProperty(error.message ?: "")
)
@ -285,19 +326,18 @@ class EditTypePropertiesViewModel(
prefilled = emptyMap()
)
createRelation(params).process(
success = { relation ->
Timber.d("Relation created: $relation")
success = { property ->
Timber.d("Property created: $property")
val objType = storeOfObjectTypes.get(vmParams.objectTypeId)
if (objType != null) {
proceedWithSetRecommendedProperties(
properties = objType.recommendedRelations + listOf(relation.id)
properties = objType.recommendedRelations + listOf(property.id)
)
}
uiPropertyEditState.value = UiEditPropertyState.Hidden
_commands.emit(EditTypePropertiesCommand.Exit)
},
failure = { error ->
Timber.e(error, "Failed to create relation")
Timber.e(error, "Failed to create property")
_errorState.value = UiEditTypePropertiesErrorState.Show(
UiEditTypePropertiesErrorState.Reason.ErrorCreatingProperty(error.message ?: "")
)

View file

@ -9,4 +9,8 @@ sealed class UiEditTypePropertiesEvent {
data object OnSaveButtonClicked : UiEditTypePropertiesEvent()
data object OnEditPropertyScreenDismissed : UiEditTypePropertiesEvent()
data class OnPropertyNameUpdate(val name: String) : UiEditTypePropertiesEvent()
data object OnPropertyFormatClick : UiEditTypePropertiesEvent()
data object OnPropertyFormatsListDismiss : UiEditTypePropertiesEvent()
data class OnPropertyFormatSelected(val format: UiEditTypePropertiesItem.Format) : UiEditTypePropertiesEvent()
}

View file

@ -14,7 +14,7 @@ data class UiEditTypePropertiesState(
companion object {
val EMPTY = UiEditTypePropertiesState(emptyList())
val DEFAULT_NEW_PROPERTY_FORMAT = RelationFormat.STATUS
val DEFAULT_NEW_PROPERTY_FORMAT = RelationFormat.LONG_TEXT
//This is a list of formats that are available for creating new properties
val PROPERTIES_FORMATS = listOf<RelationFormat>(

View file

@ -177,6 +177,12 @@ fun AddFieldScreen(
},
onPropertyNameUpdate = { name ->
event(UiEditTypePropertiesEvent.OnPropertyNameUpdate(name))
},
onFormatClick = {
event(UiEditTypePropertiesEvent.OnPropertyFormatClick)
},
onSaveButtonClicked = {
event(UiEditTypePropertiesEvent.OnSaveButtonClicked)
}
)
}
@ -201,7 +207,7 @@ private fun Section(
}
@Composable
private fun PropertyTypeItem(
fun PropertyTypeItem(
modifier: Modifier,
item: UiEditTypePropertiesItem.Format
) {

View file

@ -3,6 +3,7 @@ package com.anytypeio.anytype.feature_properties.edit
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesItem
import com.anytypeio.anytype.presentation.objects.ObjectIcon
sealed class UiEditPropertyState {
@ -16,7 +17,8 @@ sealed class UiEditPropertyState {
val formatName: String,
val formatIcon: Int?,
val format: RelationFormat,
val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList()
val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList(),
val isPossibleToUnlinkFromType: Boolean
) : Visible()
data class New(
@ -34,11 +36,20 @@ sealed class UiEditPropertyState {
val formatName: String,
val formatIcon: Int?,
val format: RelationFormat,
val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList()
val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList(),
val isPossibleToUnlinkFromType: Boolean
) : Visible()
}
}
data class UiPropertyLimitTypeItem(
val id: Id, val key: Key, val title: String, val icon: ObjectIcon
)
)
sealed class UiPropertyFormatsListState {
data class Visible(
val items: List<UiEditTypePropertiesItem.Format>
) : UiPropertyFormatsListState()
data object Hidden : UiPropertyFormatsListState()
}

View file

@ -5,11 +5,12 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
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.RoundedCornerShape
import androidx.compose.material3.DropdownMenu
@ -29,6 +30,7 @@ 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_models.Id
import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
@ -47,15 +49,16 @@ fun PropertyEditScreen(
onFormatClick: () -> Unit,
onLimitTypesClick: () -> Unit,
onPropertyNameUpdate: (String) -> Unit,
onDeleteButtonClicked: () -> Unit
onMenuUnlinkClick: (Id) -> Unit
) {
var innerValue by remember(uiState.name) { mutableStateOf(uiState.name) }
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
var isMenuExpanded by remember { mutableStateOf(false) }
var isNameChanged by remember { mutableStateOf(false) }
Column(modifier = modifier) {
Column(modifier = modifier.imePadding()) {
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
@ -77,50 +80,52 @@ fun PropertyEditScreen(
emptyName = stringResource(R.string.untitled),
onValueChange = {
innerValue = it
isNameChanged = true
onPropertyNameUpdate(it)
}
)
Spacer(modifier = Modifier.size(4.dp))
Box(
modifier = Modifier
.padding(end = 21.dp)
.size(40.dp)
.noRippleThrottledClickable {
isMenuExpanded = true
}
) {
Image(
if (uiState.isPossibleToUnlinkFromType) {
Box(
modifier = Modifier
//.padding(end = 20.dp)
.wrapContentSize()
.align(Alignment.Center),
painter = painterResource(id = R.drawable.ic_widget_three_dots),
contentDescription = "Property menu icon",
contentScale = ContentScale.None,
)
DropdownMenu(
modifier = Modifier.width(244.dp),
expanded = isMenuExpanded,
onDismissRequest = { isMenuExpanded = false },
shape = RoundedCornerShape(size = 10.dp),
containerColor = colorResource(id = R.color.background_primary),
shadowElevation = 5.dp,
) {
DropdownMenuItem(
modifier = Modifier.height(44.dp),
onClick = {
onDeleteButtonClicked()
isMenuExpanded = false
},
text = {
Text(
text = stringResource(R.string.delete),
style = BodyRegular,
color = colorResource(id = R.color.palette_system_red),
modifier = Modifier
)
.padding(end = 21.dp)
.size(40.dp)
.noRippleThrottledClickable {
isMenuExpanded = true
}
) {
Image(
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center),
painter = painterResource(id = R.drawable.ic_widget_three_dots),
contentDescription = "Property menu icon",
contentScale = ContentScale.None,
)
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 244.dp),
expanded = isMenuExpanded,
onDismissRequest = { isMenuExpanded = false },
shape = RoundedCornerShape(size = 10.dp),
containerColor = colorResource(id = R.color.background_primary),
shadowElevation = 5.dp,
) {
DropdownMenuItem(
modifier = Modifier.height(44.dp),
onClick = {
onMenuUnlinkClick(uiState.id)
isMenuExpanded = false
},
text = {
Text(
text = stringResource(R.string.property_edit_menu_unlink),
style = BodyRegular,
color = colorResource(id = R.color.palette_system_red),
modifier = Modifier
)
}
)
}
}
}
}
@ -134,7 +139,7 @@ fun PropertyEditScreen(
.padding(horizontal = 20.dp)
.noRippleThrottledClickable { onFormatClick() },
formatName = uiState.formatName,
isEditable = true,
isEditable = false,
)
Divider()
@ -156,8 +161,11 @@ fun PropertyEditScreen(
onClick = {
onSaveButtonClicked()
},
size = ButtonSize.Large
size = ButtonSize.Large,
enabled = innerValue.isNotBlank() && isNameChanged
)
Spacer(modifier = Modifier.height(32.dp))
}
}
@ -173,12 +181,13 @@ fun EditPropertyPreview() {
formatName = "Text",
formatIcon = R.drawable.ic_relation_format_date_small,
limitObjectTypes = emptyList(),
format = RelationFormat.OBJECT
format = RelationFormat.OBJECT,
isPossibleToUnlinkFromType = true
),
onSaveButtonClicked = {},
onFormatClick = {},
onLimitTypesClick = {},
onPropertyNameUpdate = {},
onDeleteButtonClicked = {},
onMenuUnlinkClick = {}
)
}

View file

@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
@ -41,7 +42,7 @@ fun PropertyNewScreen(
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
Column(modifier = modifier) {
Column(modifier = modifier.imePadding()) {
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
@ -100,8 +101,11 @@ fun PropertyNewScreen(
onClick = {
onCreateNewButtonClicked()
},
size = ButtonSize.Large
size = ButtonSize.Large,
enabled = innerValue.isNotBlank()
)
Spacer(modifier = Modifier.height(32.dp))
}
}

View file

@ -0,0 +1,89 @@
package com.anytypeio.anytype.feature_properties.edit.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
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.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.widgets.dv.DragHandle
import com.anytypeio.anytype.feature_properties.edit.UiPropertyFormatsListState
import com.anytypeio.anytype.feature_properties.R
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesItem
import com.anytypeio.anytype.feature_properties.add.ui.PropertyTypeItem
import com.anytypeio.anytype.feature_properties.add.ui.commonItemModifier
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PropertyFormatsListScreen(
uiState: UiPropertyFormatsListState.Visible,
onDismissRequest: () -> Unit,
onFormatClick: (UiEditTypePropertiesItem.Format) -> Unit,
) {
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val lazyListState = rememberLazyListState()
ModalBottomSheet(
modifier = Modifier.fillMaxWidth(),
onDismissRequest = onDismissRequest,
dragHandle = { DragHandle() },
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
containerColor = colorResource(id = R.color.background_primary),
shape = RoundedCornerShape(16.dp),
sheetState = bottomSheetState,
) {
Box(
modifier = Modifier.fillMaxWidth().height(48.dp),
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(R.string.property_select_format_title),
style = Title1,
color = colorResource(R.color.text_primary),
textAlign = TextAlign.Center
)
}
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
LazyColumn(
modifier = Modifier
.background(color = colorResource(id = R.color.background_primary)),
state = lazyListState
) {
items(
count = uiState.items.size,
key = { index -> uiState.items[index].id },
itemContent = { index ->
val item = uiState.items[index]
PropertyTypeItem(
modifier = commonItemModifier()
.clickable {
onFormatClick(item)
},
item = item
)
}
)
item {
Spacer(modifier = Modifier.height(100.dp))
}
}
}
}

View file

@ -53,7 +53,7 @@ fun PropertyScreen(
onCreateNewButtonClicked: () -> Unit = {},
onDismissRequest: () -> Unit,
onPropertyNameUpdate: (String) -> Unit,
onDeleteButtonClicked: (Id) -> Unit ={},
onMenuUnlinkClick: (Id) -> Unit ={}
) {
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
ModalBottomSheet(
@ -61,7 +61,7 @@ fun PropertyScreen(
dragHandle = { DragHandle() },
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
containerColor = colorResource(id = R.color.background_primary),
shape = RoundedCornerShape(16.dp),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
sheetState = bottomSheetState,
onDismissRequest = onDismissRequest,
) {
@ -73,16 +73,15 @@ fun PropertyScreen(
onFormatClick = onFormatClick,
onLimitTypesClick = onLimitTypesClick,
onPropertyNameUpdate = onPropertyNameUpdate,
onDeleteButtonClicked = {
onDeleteButtonClicked(uiState.id)
}
onMenuUnlinkClick = onMenuUnlinkClick
)
is UiEditPropertyState.Visible.View -> PropertyViewScreen(
modifier = Modifier.fillMaxWidth(),
uiState = uiState,
onFormatClick = onFormatClick,
onLimitTypesClick = onLimitTypesClick
onLimitTypesClick = onLimitTypesClick,
onMenuUnlinkClick = onMenuUnlinkClick
)
is UiEditPropertyState.Visible.New -> PropertyNewScreen(

View file

@ -1,12 +1,20 @@
package com.anytypeio.anytype.feature_properties.edit.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -15,14 +23,19 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
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_models.Id
import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
@Composable
@ -30,12 +43,14 @@ fun PropertyViewScreen(
modifier: Modifier,
uiState: UiEditPropertyState.Visible.View,
onFormatClick: () -> Unit,
onLimitTypesClick: () -> Unit
onLimitTypesClick: () -> Unit,
onMenuUnlinkClick: (Id) -> Unit
) {
var innerValue by remember(uiState.name) { mutableStateOf(uiState.name) }
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
var isMenuExpanded by remember { mutableStateOf(false) }
Column(modifier = modifier) {
Spacer(modifier = Modifier.height(20.dp))
@ -59,7 +74,52 @@ fun PropertyViewScreen(
emptyName = stringResource(R.string.untitled),
onValueChange = { innerValue = it }
)
Spacer(modifier = Modifier.size(20.dp))
if (uiState.isPossibleToUnlinkFromType) {
Spacer(modifier = Modifier.size(4.dp))
Box(
modifier = Modifier
.padding(end = 21.dp)
.size(40.dp)
.noRippleThrottledClickable {
isMenuExpanded = true
}
) {
Image(
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center),
painter = painterResource(id = R.drawable.ic_widget_three_dots),
contentDescription = "Property menu icon",
contentScale = ContentScale.None,
)
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 244.dp),
expanded = isMenuExpanded,
onDismissRequest = { isMenuExpanded = false },
shape = RoundedCornerShape(size = 10.dp),
containerColor = colorResource(id = R.color.background_primary),
shadowElevation = 5.dp,
) {
DropdownMenuItem(
modifier = Modifier.height(44.dp),
onClick = {
onMenuUnlinkClick(uiState.id)
isMenuExpanded = false
},
text = {
Text(
text = stringResource(R.string.property_edit_menu_unlink),
style = BodyRegular,
color = colorResource(id = R.color.palette_system_red),
modifier = Modifier
)
}
)
}
}
} else {
Spacer(modifier = Modifier.size(20.dp))
}
}
Spacer(modifier = Modifier.height(8.dp))
@ -83,6 +143,8 @@ fun PropertyViewScreen(
)
Divider()
}
Spacer(modifier = Modifier.height(32.dp))
}
}
@ -97,9 +159,11 @@ fun MyPreviewView() {
name = "View property",
formatName = "Text",
formatIcon = R.drawable.ic_relation_format_date_small,
format = RelationFormat.FILE
format = RelationFormat.FILE,
isPossibleToUnlinkFromType = true
),
onFormatClick = {},
onLimitTypesClick = {}
onLimitTypesClick = {},
onMenuUnlinkClick = {}
)
}

View file

@ -1865,12 +1865,12 @@ Please provide specific details of your needs here.</string>
<string name="object_type_templates_menu_edit">Edit</string>
<string name="object_type_templates_menu_duplicate">Duplicate</string>
<string name="object_type_templates_menu_delete">Delete</string>
<string name="object_type_fields_section_header">Header</string>
<string name="object_type_fields_section_fields_menu">Sidebar</string>
<string name="object_type_fields_section_header">Shown in object header</string>
<string name="object_type_fields_section_fields_menu">Shown in properties menu</string>
<string name="object_type_fields_section_hidden">Hidden</string>
<string name="object_type_fields_section_file">File</string>
<string name="object_type_fields_section_local_fields">Local fields</string>
<string name="object_type_fields_title">Fields</string>
<string name="object_type_fields_section_local_fields">Local properties</string>
<string name="object_type_fields_title">Properties</string>
<string name="object_type_fields_info_text">You\'re editing type</string>
<string name="object_type_fields_edit_field">Edit field</string>
<string name="object_type_fields_new_field">New field</string>
@ -1921,7 +1921,7 @@ Please provide specific details of your needs here.</string>
<string name="migration_migration_failed">Migration failed</string>
<string name="migration_error_please_free_up_space_and_run_the_process_again">Please free up space and run the process again.</string>
<string name="fields_screen_title">Fields</string>
<string name="fields_screen_title">Properties</string>
<string name="field_text_title">Text</string>
<string name="field_text_empty">Enter</string>
<string name="field_number_title">Number</string>
@ -1966,9 +1966,8 @@ Please provide specific details of your needs here.</string>
<string name="space_settings_save_button">Save</string>
<string name="object_type_add_property_screen_title">Add property</string>
<string name="object_type_add_property_screen_section">Properties types</string>
<string name="object_type_add_property_screen_create">Create property \'%1$s\'"</string>
<string name="object_type_add_property_screen_section_types">Properties types"</string>
<string name="object_type_add_property_screen_section_types">Properties formats"</string>
<string name="object_type_add_property_screen_section_existing">Existing properties"</string>
<string name="object_type_add_property_screen_search_hint">Search or create new"</string>
@ -1979,4 +1978,8 @@ Please provide specific details of your needs here.</string>
<string name="add_property_error_update">Error while updating property</string>
<string name="add_property_error_add">Error while adding property to type</string>
<string name="property_select_format_title">Select property format</string>
<string name="property_edit_menu_delete">Delete from space</string>
<string name="property_edit_menu_unlink">Unlink from type</string>
</resources>