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:
parent
d0c3412e8c
commit
f69d922791
12 changed files with 425 additions and 86 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -46,6 +46,7 @@ class SpaceTypesFragment : BaseComposeFragment() {
|
|||
onBackPressed = vm::onBackClicked,
|
||||
onTypeClicked = vm::onTypeClicked,
|
||||
onAddIconClicked = vm::onCreateNewTypeClicked,
|
||||
onMoveToBin = vm::onMoveToBin
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
|
@ -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
|
||||
|
|
10
core-ui/src/main/res/drawable/ic_arrow_right_18.xml
Normal file
10
core-ui/src/main/res/drawable/ic_arrow_right_18.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
<path
|
||||
android:pathData="M6.22,14.789C5.927,14.508 5.927,14.052 6.22,13.771L11.189,9L6.22,4.229C5.927,3.948 5.927,3.492 6.22,3.211C6.513,2.93 6.987,2.93 7.28,3.211L13.311,9L7.28,14.789C6.987,15.07 6.513,15.07 6.22,14.789Z"
|
||||
android:fillColor="@color/glyph_active"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 = {}
|
||||
)
|
||||
}
|
|
@ -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 =
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2034,6 +2034,12 @@ Please provide specific details of your needs here.</string>
|
|||
<string name="space_settings_auto_create_widgets">Auto Create Type Widgets</string>
|
||||
|
||||
<string name="space_types_screen_title">Object types</string>
|
||||
<string name="space_types_screen_section_my_types">My Types</string>
|
||||
<string name="space_types_screen_section_system_types">System Types</string>
|
||||
|
||||
<string name="space_properties_screen_title">Properties</string>
|
||||
<string name="space_properties_screen_section_my_types">My Properties</string>
|
||||
<string name="space_properties_screen_section_system_types">System Properties</string>
|
||||
<string name="space_properties_screen_menu_move_to_bin">Move to bin</string>
|
||||
|
||||
</resources>
|
|
@ -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 <T : ViewModel> create(modelClass: Class<T>): 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<UiSpaceTypeItem>)
|
||||
|
||||
data class UiSpaceTypeItem(
|
||||
val id: Id,
|
||||
val name: String,
|
||||
val icon: ObjectIcon.TypeIcon
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue