diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/PrimitivesObjectTypeDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/PrimitivesObjectTypeDI.kt index d7ae4c0c18..4c8718c204 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/PrimitivesObjectTypeDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/PrimitivesObjectTypeDI.kt @@ -222,6 +222,14 @@ interface SpaceTypesComponent { @Module object SpaceTypesModule { + @JvmStatic + @PerScreen + @Provides + fun getSetObjectListIsArchived( + repo: BlockRepository, + dispatchers: AppCoroutineDispatchers + ): SetObjectListIsArchived = SetObjectListIsArchived(repo, dispatchers) + @Module interface Declarations { @PerScreen @@ -268,6 +276,14 @@ interface SpacePropertiesComponent { @Module object SpacePropertiesModule { + @JvmStatic + @PerScreen + @Provides + fun getSetObjectListIsArchived( + repo: BlockRepository, + dispatchers: AppCoroutineDispatchers + ): SetObjectListIsArchived = SetObjectListIsArchived(repo, dispatchers) + @JvmStatic @Provides @PerScreen diff --git a/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpacePropertiesFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpacePropertiesFragment.kt index 92aa555537..86476270a0 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpacePropertiesFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpacePropertiesFragment.kt @@ -49,7 +49,8 @@ class SpacePropertiesFragment : BaseComposeFragment() { uiState = vm.uiItemsState.collectAsStateWithLifecycle().value, onBackPressed = vm::onBackClicked, onPropertyClicked = vm::onPropertyClicked, - onAddIconClicked = vm::onCreateNewPropertyClicked + onAddIconClicked = vm::onCreateNewPropertyClicked, + onMoveToBin = vm::onMoveToBinProperty ) SpacePropertyScreen( uiState = vm.uiEditPropertyScreen.collectAsStateWithLifecycle().value diff --git a/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpaceTypesFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpaceTypesFragment.kt index 062d9991f8..de7da0142f 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpaceTypesFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/primitives/SpaceTypesFragment.kt @@ -46,6 +46,7 @@ class SpaceTypesFragment : BaseComposeFragment() { onBackPressed = vm::onBackClicked, onTypeClicked = vm::onTypeClicked, onAddIconClicked = vm::onCreateNewTypeClicked, + onMoveToBin = vm::onMoveToBin ) } LaunchedEffect(Unit) { diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/fields/FieldsListScreen.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/fields/FieldsListScreen.kt index a82aa7d792..910543029a 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/fields/FieldsListScreen.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/fields/FieldsListScreen.kt @@ -336,8 +336,8 @@ private fun SectionLocal( ) { val text = stringResource(id = R.string.object_properties_section_local) val iconRes = when (item) { - is Model.Section.Local.Shown -> R.drawable.ic_arrow_up_18 - is Model.Section.Local.Unshown -> R.drawable.ic_list_arrow_18 + is Model.Section.Local.Shown -> R.drawable.ic_list_arrow_18 + is Model.Section.Local.Unshown -> R.drawable.ic_arrow_right_18 } Box( modifier = Modifier @@ -409,8 +409,8 @@ private fun SectionHidden( ) { val text = stringResource(id = R.string.object_properties_section_hidden) val iconRes = when (item) { - is Model.Section.Hidden.Shown -> R.drawable.ic_arrow_up_18 - is Model.Section.Hidden.Unshown -> R.drawable.ic_list_arrow_18 + is Model.Section.Hidden.Shown -> R.drawable.ic_list_arrow_18 + is Model.Section.Hidden.Unshown -> R.drawable.ic_arrow_right_18 } Box( modifier = Modifier diff --git a/core-ui/src/main/res/drawable/ic_arrow_right_18.xml b/core-ui/src/main/res/drawable/ic_arrow_right_18.xml new file mode 100644 index 0000000000..bf81934c60 --- /dev/null +++ b/core-ui/src/main/res/drawable/ic_arrow_right_18.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/fields/ui/InfoScreen.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/fields/ui/InfoScreen.kt index bc1602f8ef..1ee4c78d15 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/fields/ui/InfoScreen.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/fields/ui/InfoScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.unit.dp import com.anytypeio.anytype.core_ui.R import com.anytypeio.anytype.core_ui.common.DefaultPreviews import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular +import com.anytypeio.anytype.core_ui.views.ButtonPrimary import com.anytypeio.anytype.core_ui.views.ButtonSecondary import com.anytypeio.anytype.core_ui.views.ButtonSize import com.anytypeio.anytype.core_ui.views.HeadlineHeading @@ -91,12 +92,12 @@ fun LocalInfoScreen( text = stringResource(description) ) Spacer(modifier = Modifier.height(30.dp)) - ButtonSecondary( + ButtonPrimary( modifier = Modifier .fillMaxWidth() .padding(horizontal = 20.dp), text = stringResource(R.string.object_type_fields_local_info_button), - size = ButtonSize.LargeSecondary, + size = ButtonSize.Large, onClick = { onDismiss() } diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/space/SpaceTypesListScreen.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/space/SpaceTypesListScreen.kt index 54dbf4dcbf..ce561f9295 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/space/SpaceTypesListScreen.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/space/SpaceTypesListScreen.kt @@ -1,9 +1,10 @@ package com.anytypeio.anytype.feature_object_type.ui.space 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 @@ -21,10 +22,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 @@ -34,14 +39,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.common.DefaultPreviews 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_ui.widgets.ListWidgetObjectIcon import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK import com.anytypeio.anytype.presentation.objects.ObjectIcon @@ -52,9 +60,10 @@ import com.anytypeio.anytype.presentation.types.UiSpaceTypesScreenState @Composable fun SpaceTypesListScreen( uiState: UiSpaceTypesScreenState, - onTypeClicked: (UiSpaceTypeItem) -> Unit, + onTypeClicked: (UiSpaceTypeItem.Type) -> Unit, onBackPressed: () -> Unit, - onAddIconClicked: () -> Unit + onAddIconClicked: () -> Unit, + onMoveToBin: (UiSpaceTypeItem.Type) -> Unit ) { Column( modifier = Modifier @@ -91,17 +100,24 @@ fun SpaceTypesListScreen( key = { index -> items[index].id }, itemContent = { val item = items[it] - Type( - modifier = Modifier - .fillMaxWidth() - .height(52.dp) - .padding(start = 12.dp, end = 20.dp) - .clickable { - onTypeClicked(item) - }, - item = item - ) - Divider() + when (item) { + is UiSpaceTypeItem.Section -> { + Section(item) + } + + is UiSpaceTypeItem.Type -> { + Type( + modifier = Modifier + .fillMaxWidth() + .height(52.dp) + .padding(start = 12.dp, end = 20.dp), + item = item, + onTypeClicked = onTypeClicked, + onMoveToBin = onMoveToBin + ) + Divider() + } + } } ) item { @@ -115,13 +131,48 @@ fun SpaceTypesListScreen( } } +@Composable +private fun Section(section: UiSpaceTypeItem.Section) { + val text = when (section) { + is UiSpaceTypeItem.Section.MyTypes -> + stringResource(R.string.space_types_screen_section_my_types) + + is UiSpaceTypeItem.Section.System -> + stringResource(R.string.space_types_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 Type( modifier: Modifier, - item: UiSpaceTypeItem + item: UiSpaceTypeItem.Type, + onTypeClicked: (UiSpaceTypeItem.Type) -> Unit, + onMoveToBin: (UiSpaceTypeItem.Type) -> Unit ) { + val isMenuExpanded = remember { mutableStateOf(false) } Row( - modifier = modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth() + .combinedClickable( + onClick = { onTypeClicked(item) }, + onLongClick = { isMenuExpanded.value = true } + ), verticalAlignment = CenterVertically ) { ListWidgetObjectIcon( @@ -141,6 +192,51 @@ private fun Type( 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: UiSpaceTypeItem.Type, + showMenu: Boolean, + onDismissRequest: () -> Unit, + onMoveToBin: (UiSpaceTypeItem.Type) -> 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) + }, + ) } } @@ -208,7 +304,8 @@ fun SpaceTypesListScreenPreview() { SpaceTypesListScreen( uiState = UiSpaceTypesScreenState( items = listOf( - UiSpaceTypeItem( + UiSpaceTypeItem.Section.MyTypes(), + UiSpaceTypeItem.Type( id = "1", name = "Type 1", icon = ObjectIcon.TypeIcon.Default( @@ -216,7 +313,8 @@ fun SpaceTypesListScreenPreview() { color = CustomIconColor.Teal ) ), - UiSpaceTypeItem( + UiSpaceTypeItem.Section.System(), + UiSpaceTypeItem.Type( id = "2", name = "Type 2", icon = ObjectIcon.TypeIcon.Default( @@ -228,6 +326,7 @@ fun SpaceTypesListScreenPreview() { ), onBackPressed = {}, onTypeClicked = {}, - onAddIconClicked = {} + onAddIconClicked = {}, + onMoveToBin = {} ) } \ No newline at end of file diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/viewmodel/ObjectTypeViewModel.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/viewmodel/ObjectTypeViewModel.kt index 876022a414..b37926b51f 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/viewmodel/ObjectTypeViewModel.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/viewmodel/ObjectTypeViewModel.kt @@ -351,9 +351,10 @@ class ObjectTypeViewModel( (uiTitleAndIconUpdateState.value as? UiTypeSetupTitleAndIconState.Visible.EditType)?.let { uiTitleAndIconUpdateState.value = it.copy(icon = newIcon) } - if (objectPermissions.canDelete) { - uiEditButtonState.value = UiEditButton.Visible - } + //turn off button, we give Move to Bin logic in Library now +// if (objectPermissions.canDelete) { +// uiEditButtonState.value = UiEditButton.Visible +// } objType.recommendedLayout?.let { layout -> if (_objectTypePermissionsState.value?.canChangeRecommendedLayoutForThisType == true) { uiHorizontalButtonsState.value = diff --git a/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/SpacePropertiesViewModel.kt b/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/SpacePropertiesViewModel.kt index b549c2a5d6..d696e5adb8 100644 --- a/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/SpacePropertiesViewModel.kt +++ b/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/SpacePropertiesViewModel.kt @@ -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 { + private suspend fun computeLimitTypes(item: UiSpacePropertyItem.Item): List { 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 create(modelClass: Class): 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 -) \ No newline at end of file +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, + val isPossibleMoveToBin: Boolean = false + ) : UiSpacePropertyItem() +} \ No newline at end of file diff --git a/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/ui/SpacePropertiesListScreen.kt b/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/ui/SpacePropertiesListScreen.kt index 94768d8300..142863fca8 100644 --- a/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/ui/SpacePropertiesListScreen.kt +++ b/feature-properties/src/main/java/com/anytypeio/anytype/feature_properties/space/ui/SpacePropertiesListScreen.kt @@ -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) + }, + ) } } diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml index 3b5f343ac4..e4ad6e14bd 100644 --- a/localization/src/main/res/values/strings.xml +++ b/localization/src/main/res/values/strings.xml @@ -2034,6 +2034,12 @@ Please provide specific details of your needs here. Auto Create Type Widgets Object types + My Types + System Types + Properties + My Properties + System Properties + Move to bin \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/types/SpaceTypesViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/types/SpaceTypesViewModel.kt index 5b840bc9d0..3e75bc4efc 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/types/SpaceTypesViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/types/SpaceTypesViewModel.kt @@ -6,9 +6,11 @@ import androidx.lifecycle.viewModelScope import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType -import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.primitives.SpaceId +import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction +import com.anytypeio.anytype.domain.base.fold import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider +import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes import com.anytypeio.anytype.domain.primitives.FieldParser import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate @@ -29,6 +31,7 @@ class SpaceTypesViewModel( private val fieldParser: FieldParser, private val storeOfObjectTypes: StoreOfObjectTypes, private val userPermissionProvider: UserPermissionProvider, + private val setObjectListIsArchived: SetObjectListIsArchived ) : ViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate { val uiItemsState = @@ -60,19 +63,64 @@ class SpaceTypesViewModel( .collectLatest { event -> val allTypes = storeOfObjectTypes.getAll().mapNotNull { objectType -> - val resolvedLayout = objectType.recommendedLayout ?: return@mapNotNull null + val resolvedLayout = + objectType.recommendedLayout ?: return@mapNotNull null if (notAllowedTypesLayouts.contains(resolvedLayout)) { return@mapNotNull null } else { - objectType.toUiItem() + objectType } } - .sortedBy { it.name } - uiItemsState.value = UiSpaceTypesScreenState(allTypes) + val (myTypes, systemTypes) = allTypes.partition { + !it.restrictions.contains( + ObjectRestriction.DELETE + ) + } + + val list = buildList { + add(UiSpaceTypeItem.Section.MyTypes()) + addAll(myTypes.map { type -> + UiSpaceTypeItem.Type( + id = type.id, + name = fieldParser.getObjectName(type), + icon = type.objectIcon(), + isPossibleMoveToBin = true + ) + }.sortedBy { it.name }) + add(UiSpaceTypeItem.Section.System()) + addAll(systemTypes.map { type -> + UiSpaceTypeItem.Type( + id = type.id, + name = fieldParser.getObjectName(type), + icon = type.objectIcon(), + isPossibleMoveToBin = false + ) + }.sortedBy { it.name }) + } + + uiItemsState.value = UiSpaceTypesScreenState(list) } } } + fun onMoveToBin(item: UiSpaceTypeItem.Type) { + val typeId = item.id + viewModelScope.launch { + val params = SetObjectListIsArchived.Params( + targets = listOf(typeId), + isArchived = true + ) + setObjectListIsArchived.async(params).fold( + onSuccess = { + Timber.d("Object Type $typeId moved to bin") + }, + onFailure = { + Timber.e(it, "Error while moving pbject type $typeId to bin") + } + ) + } + } + fun onBackClicked() { viewModelScope.launch { commands.emit(Command.Back) @@ -80,7 +128,7 @@ class SpaceTypesViewModel( } fun onCreateNewTypeClicked() { - if (permission.value?.isOwnerOrEditor() == true) { + if (permission.value?.isOwnerOrEditor() == true) { viewModelScope.launch { commands.emit(Command.CreateNewType(vmParams.spaceId.id)) } @@ -91,7 +139,7 @@ class SpaceTypesViewModel( } } - fun onTypeClicked(type: UiSpaceTypeItem) { + fun onTypeClicked(type: UiSpaceTypeItem.Type) { viewModelScope.launch { commands.emit( Command.OpenType( @@ -109,13 +157,6 @@ class SpaceTypesViewModel( data class ShowToast(val message: String) : Command() } - private fun ObjectWrapper.Type.toUiItem() = UiSpaceTypeItem( - id = id, - name = fieldParser.getObjectName(this), - icon = this.objectIcon() - - ) - data class VmParams( val spaceId: SpaceId ) @@ -127,7 +168,8 @@ class SpaceTypesVmFactory @Inject constructor( private val analytics: Analytics, private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate, private val userPermissionProvider: UserPermissionProvider, - private val fieldParser: FieldParser + private val fieldParser: FieldParser, + private val setObjectListIsArchived: SetObjectListIsArchived ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T = @@ -137,14 +179,26 @@ class SpaceTypesVmFactory @Inject constructor( analytics = analytics, analyticSpaceHelperDelegate = analyticSpaceHelperDelegate, userPermissionProvider = userPermissionProvider, - fieldParser = fieldParser + fieldParser = fieldParser, + setObjectListIsArchived = setObjectListIsArchived ) as T } data class UiSpaceTypesScreenState(val items: List) -data class UiSpaceTypeItem( - val id: Id, - val name: String, - val icon: ObjectIcon.TypeIcon -) \ No newline at end of file + +sealed class UiSpaceTypeItem { + abstract val id: Id + + data class Type( + override val id: Id, + val name: String, + val icon: ObjectIcon.TypeIcon, + val isPossibleMoveToBin: Boolean = false + ) : UiSpaceTypeItem() + + sealed class Section : UiSpaceTypeItem() { + data class MyTypes(override val id: Id = "section_my_types") : Section() + data class System(override val id: Id = "section_system") : Section() + } +} \ No newline at end of file