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

DROID-3571 Primitives | Space types and properties, sections ui (#2319)

This commit is contained in:
Konstantin Ivanov 2025-04-15 18:42:51 +02:00 committed by GitHub
parent d0c3412e8c
commit f69d922791
Signed by: github
GPG key ID: B5690EEEBB952194
12 changed files with 425 additions and 86 deletions

View file

@ -9,10 +9,12 @@ import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.primitives.RelationKey
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.objects.mapLimitObjectTypes
@ -53,7 +55,8 @@ class SpacePropertiesViewModel(
private val storeOfRelations: StoreOfRelations,
private val stringResourceProvider: StringResourceProvider,
private val setObjectDetails: SetObjectDetails,
private val createRelation: CreateRelation
private val createRelation: CreateRelation,
private val setObjectListIsArchived: SetObjectListIsArchived
) : ViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
// Main UI states
@ -76,24 +79,64 @@ class SpacePropertiesViewModel(
storeOfRelations.trackChanges()
.collectLatest { event ->
val allProperties = storeOfRelations.getAll().mapNotNull { property ->
if (property.isHidden == true) {
if (property.isHidden == true || property.isDeleted == true || property.isArchived == true) {
null
} else {
UiSpacePropertyItem(
property
}
}
val (myProperties, systemProperties) =
allProperties.partition { !it.restrictions.contains(ObjectRestriction.DELETE) }
val list = buildList {
add(UiSpacePropertyItem.Section.MyProperties())
addAll(myProperties.map { property ->
UiSpacePropertyItem.Item(
id = property.id,
key = RelationKey(property.key),
name = property.name.orEmpty(),
format = property.format,
isEditableField = fieldParser.isPropertyEditable(property),
limitObjectTypes = storeOfObjectTypes.mapLimitObjectTypes(property = property)
limitObjectTypes = storeOfObjectTypes.mapLimitObjectTypes(property = property),
isPossibleMoveToBin = true
)
}
}.sortedBy { it.name }
uiItemsState.value = UiSpacePropertiesScreenState(allProperties)
}.sortedBy { it.name })
add(UiSpacePropertyItem.Section.SystemProperties())
addAll(systemProperties.map { property ->
UiSpacePropertyItem.Item(
id = property.id,
key = RelationKey(property.key),
name = property.name.orEmpty(),
format = property.format,
isEditableField = fieldParser.isPropertyEditable(property),
limitObjectTypes = storeOfObjectTypes.mapLimitObjectTypes(property = property),
isPossibleMoveToBin = false
)
}.sortedBy { it.name })
}
uiItemsState.value = UiSpacePropertiesScreenState(list)
}
}
}
fun onMoveToBinProperty(item: UiSpacePropertyItem.Item) {
val propertyId = item.id
viewModelScope.launch {
val params = SetObjectListIsArchived.Params(
targets = listOf(propertyId),
isArchived = true
)
setObjectListIsArchived.async(params).fold(
onSuccess = {
Timber.d("Property $propertyId moved to bin")
},
onFailure = {
Timber.e(it, "Error while moving property $propertyId to bin")
}
)
}
}
fun onBackClicked() {
viewModelScope.launch {
commands.emit(Command.Back)
@ -124,7 +167,7 @@ class SpacePropertiesViewModel(
}
}
fun onPropertyClicked(item: UiSpacePropertyItem) {
fun onPropertyClicked(item: UiSpacePropertyItem.Item) {
viewModelScope.launch {
val computedLimitTypes = computeLimitTypes(item = item)
val formatName = stringResourceProvider.getPropertiesFormatPrettyString(item.format)
@ -158,7 +201,7 @@ class SpacePropertiesViewModel(
}
}
private suspend fun computeLimitTypes(item: UiSpacePropertyItem): List<UiPropertyLimitTypeItem> {
private suspend fun computeLimitTypes(item: UiSpacePropertyItem.Item): List<UiPropertyLimitTypeItem> {
return item.limitObjectTypes.mapNotNull { id ->
storeOfObjectTypes.get(id = id)?.let { objType ->
UiPropertyLimitTypeItem(
@ -329,7 +372,8 @@ class SpacePropertiesVmFactory @Inject constructor(
private val storeOfRelations: StoreOfRelations,
private val stringResourceProvider: StringResourceProvider,
private val setObjectDetails: SetObjectDetails,
private val createRelation: CreateRelation
private val createRelation: CreateRelation,
private val setObjectListIsArchived: SetObjectListIsArchived
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T =
@ -343,7 +387,8 @@ class SpacePropertiesVmFactory @Inject constructor(
storeOfRelations = storeOfRelations,
stringResourceProvider = stringResourceProvider,
setObjectDetails = setObjectDetails,
createRelation = createRelation
createRelation = createRelation,
setObjectListIsArchived = setObjectListIsArchived
) as T
}
@ -355,11 +400,22 @@ data class UiSpacePropertiesScreenState(
}
}
data class UiSpacePropertyItem(
val id: Id,
val key: RelationKey,
val name: String,
val format: RelationFormat,
val isEditableField: Boolean,
val limitObjectTypes: List<Id>
)
sealed class UiSpacePropertyItem{
abstract val id: Id
sealed class Section : UiSpacePropertyItem() {
data class MyProperties(override val id: Id = "section_my_properties") : Section()
data class SystemProperties(override val id: Id = "section_system_properties") : Section()
}
data class Item(
override val id: Id,
val key: RelationKey,
val name: String,
val format: RelationFormat,
val isEditableField: Boolean,
val limitObjectTypes: List<Id>,
val isPossibleMoveToBin: Boolean = false
) : UiSpacePropertyItem()
}

View file

@ -1,9 +1,10 @@
package com.anytypeio.anytype.feature_properties.space.ui
import android.os.Build
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -24,10 +25,14 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
@ -38,14 +43,17 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
import com.anytypeio.anytype.core_ui.extensions.swapList
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.Caption1Medium
import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.views.UxSmallTextRegular
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
import com.anytypeio.anytype.feature_properties.space.UiSpacePropertiesScreenState
import com.anytypeio.anytype.feature_properties.space.UiSpacePropertyItem
@ -53,9 +61,10 @@ import com.anytypeio.anytype.feature_properties.space.UiSpacePropertyItem
@Composable
fun SpacePropertiesListScreen(
uiState: UiSpacePropertiesScreenState,
onPropertyClicked: (UiSpacePropertyItem) -> Unit,
onPropertyClicked: (UiSpacePropertyItem.Item) -> Unit,
onBackPressed: () -> Unit,
onAddIconClicked: () -> Unit
onAddIconClicked: () -> Unit,
onMoveToBin: (UiSpacePropertyItem.Item) -> Unit
) {
Column(
modifier = Modifier
@ -93,17 +102,23 @@ fun SpacePropertiesListScreen(
key = { index -> items[index].id },
itemContent = {
val item = items[it]
Relation(
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.padding(start = 20.dp, end = 20.dp)
.clickable {
onPropertyClicked(item)
},
item = item
)
Divider()
when (item ) {
is UiSpacePropertyItem.Item -> {
Property(
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.padding(start = 20.dp, end = 20.dp),
item = item,
onPropertyClicked = onPropertyClicked,
onMoveToBin = onMoveToBin
)
Divider()
}
is UiSpacePropertyItem.Section -> {
Section(item)
}
}
}
)
item {
@ -118,12 +133,47 @@ fun SpacePropertiesListScreen(
}
@Composable
private fun Relation(
private fun Section(section: UiSpacePropertyItem.Section) {
val text = when (section) {
is UiSpacePropertyItem.Section.MyProperties ->
stringResource(R.string.space_properties_screen_section_my_types)
is UiSpacePropertyItem.Section.SystemProperties ->
stringResource(R.string.space_properties_screen_section_system_types)
}
Column {
Box(
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
) {
Text(
modifier = Modifier
.padding(bottom = 8.dp, start = 20.dp)
.align(Alignment.BottomStart),
text = text,
style = Caption1Medium,
color = colorResource(R.color.text_secondary),
)
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun Property(
modifier: Modifier,
item: UiSpacePropertyItem
item: UiSpacePropertyItem.Item,
onPropertyClicked: (UiSpacePropertyItem.Item) -> Unit,
onMoveToBin: (UiSpacePropertyItem.Item) -> Unit
) {
val isMenuExpanded = remember { mutableStateOf(false) }
Row(
modifier = modifier.fillMaxWidth(),
modifier = modifier
.fillMaxWidth()
.combinedClickable(
onClick = { onPropertyClicked(item) },
onLongClick = { isMenuExpanded.value = true }
),
verticalAlignment = CenterVertically
) {
val icon = item.format.simpleIcon()
@ -143,6 +193,50 @@ private fun Relation(
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (item.isPossibleMoveToBin) {
ItemDropDownMenu(
item = item,
showMenu = isMenuExpanded.value,
onDismissRequest = { isMenuExpanded.value = false },
onMoveToBin = {
isMenuExpanded.value = false
onMoveToBin(it)
}
)
}
}
}
@Composable
fun ItemDropDownMenu(
item: UiSpacePropertyItem.Item,
showMenu: Boolean,
onDismissRequest: () -> Unit,
onMoveToBin: (UiSpacePropertyItem.Item) -> Unit
) {
DropdownMenu(
modifier = Modifier
.width(244.dp),
expanded = showMenu,
offset = DpOffset(x = 0.dp, y = 0.dp),
onDismissRequest = {
onDismissRequest()
},
shape = RoundedCornerShape(10.dp),
containerColor = colorResource(id = R.color.background_secondary),
) {
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.space_properties_screen_menu_move_to_bin),
style = UxSmallTextRegular,
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
onMoveToBin(item)
},
)
}
}