mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3426 Space settings | Type and properties library (#2247)
This commit is contained in:
parent
c45901934a
commit
cd8eb2b1e9
21 changed files with 1176 additions and 260 deletions
|
@ -1,5 +0,0 @@
|
|||
package com.anytypeio.anytype.feature_properties
|
||||
|
||||
class EditSpacePropertiesViewModel {
|
||||
//todo: implement later
|
||||
}
|
|
@ -206,7 +206,7 @@ class EditTypePropertiesViewModel(
|
|||
formatIcon = format.simpleIcon(),
|
||||
format = format,
|
||||
showLimitTypes = false,
|
||||
limitObjectTypes = getAllObjectTypesByFormat(format)
|
||||
limitObjectTypes = format.getAllObjectTypesByFormat(storeOfObjectTypes)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ class EditTypePropertiesViewModel(
|
|||
formatIcon = format.simpleIcon(),
|
||||
format = format,
|
||||
showLimitTypes = false,
|
||||
limitObjectTypes = getAllObjectTypesByFormat(format)
|
||||
limitObjectTypes = format.getAllObjectTypesByFormat(storeOfObjectTypes)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ class EditTypePropertiesViewModel(
|
|||
),
|
||||
formatIcon = newFormat.simpleIcon(),
|
||||
format = newFormat,
|
||||
limitObjectTypes = getAllObjectTypesByFormat(newFormat),
|
||||
limitObjectTypes = newFormat.getAllObjectTypesByFormat(storeOfObjectTypes),
|
||||
selectedLimitTypeIds = emptyList(),
|
||||
showLimitTypes = false
|
||||
)
|
||||
|
@ -408,39 +408,6 @@ class EditTypePropertiesViewModel(
|
|||
}
|
||||
//endregion
|
||||
|
||||
//region Limit Object Types
|
||||
|
||||
private suspend fun getAllObjectTypesByFormat(format: RelationFormat): List<UiPropertyLimitTypeItem> {
|
||||
if (format != RelationFormat.OBJECT) return emptyList()
|
||||
return storeOfObjectTypes.getAll().mapNotNull { type ->
|
||||
when (type.recommendedLayout) {
|
||||
ObjectType.Layout.RELATION,
|
||||
ObjectType.Layout.DASHBOARD,
|
||||
ObjectType.Layout.SPACE,
|
||||
ObjectType.Layout.RELATION_OPTION_LIST,
|
||||
ObjectType.Layout.RELATION_OPTION,
|
||||
ObjectType.Layout.SPACE_VIEW,
|
||||
ObjectType.Layout.CHAT,
|
||||
ObjectType.Layout.DATE,
|
||||
ObjectType.Layout.OBJECT_TYPE,
|
||||
ObjectType.Layout.CHAT_DERIVED,
|
||||
ObjectType.Layout.TAG -> {
|
||||
null
|
||||
}
|
||||
else -> {
|
||||
UiPropertyLimitTypeItem(
|
||||
id = type.id,
|
||||
name = type.name.orEmpty(),
|
||||
icon = type.objectIcon(),
|
||||
uniqueKey = type.uniqueKey
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Commands
|
||||
sealed class EditTypePropertiesCommand {
|
||||
data object Exit : EditTypePropertiesCommand()
|
||||
|
@ -452,3 +419,34 @@ class EditTypePropertiesViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun RelationFormat.getAllObjectTypesByFormat(
|
||||
storeOfObjectTypes: StoreOfObjectTypes
|
||||
): List<UiPropertyLimitTypeItem> {
|
||||
if (this != RelationFormat.OBJECT) return emptyList()
|
||||
return storeOfObjectTypes.getAll().mapNotNull { type ->
|
||||
when (type.recommendedLayout) {
|
||||
ObjectType.Layout.RELATION,
|
||||
ObjectType.Layout.DASHBOARD,
|
||||
ObjectType.Layout.SPACE,
|
||||
ObjectType.Layout.RELATION_OPTION_LIST,
|
||||
ObjectType.Layout.RELATION_OPTION,
|
||||
ObjectType.Layout.SPACE_VIEW,
|
||||
ObjectType.Layout.CHAT,
|
||||
ObjectType.Layout.DATE,
|
||||
ObjectType.Layout.OBJECT_TYPE,
|
||||
ObjectType.Layout.CHAT_DERIVED,
|
||||
ObjectType.Layout.TAG -> {
|
||||
null
|
||||
}
|
||||
else -> {
|
||||
UiPropertyLimitTypeItem(
|
||||
id = type.id,
|
||||
name = type.name.orEmpty(),
|
||||
icon = type.objectIcon(),
|
||||
uniqueKey = type.uniqueKey
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,365 @@
|
|||
package com.anytypeio.anytype.feature_properties.space
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
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_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.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.objects.mapLimitObjectTypes
|
||||
import com.anytypeio.anytype.domain.primitives.FieldParser
|
||||
import com.anytypeio.anytype.domain.relations.CreateRelation
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesItem.Format
|
||||
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesState
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState.Visible.New
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState.Visible.View
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState.Visible.Edit
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiPropertyFormatsListState
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiPropertyFormatsListState.Hidden
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiPropertyFormatsListState.Visible
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiPropertyLimitTypeItem
|
||||
import com.anytypeio.anytype.feature_properties.getAllObjectTypesByFormat
|
||||
import com.anytypeio.anytype.feature_properties.space.SpacePropertiesViewModel.Command.*
|
||||
import com.anytypeio.anytype.feature_properties.space.ui.SpacePropertiesEvent
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.mapper.objectIcon
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.sortedBy
|
||||
import kotlin.text.orEmpty
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class SpacePropertiesViewModel(
|
||||
private val vmParams: VmParams,
|
||||
private val analytics: Analytics,
|
||||
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
|
||||
private val fieldParser: FieldParser,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val userPermissionProvider: UserPermissionProvider,
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val createRelation: CreateRelation
|
||||
) : ViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
|
||||
|
||||
// Main UI states
|
||||
val uiItemsState =
|
||||
MutableStateFlow<UiSpacePropertiesScreenState>(UiSpacePropertiesScreenState.Empty)
|
||||
val uiEditPropertyScreen = MutableStateFlow<UiEditPropertyState>(UiEditPropertyState.Hidden)
|
||||
val uiPropertyFormatsListState =
|
||||
MutableStateFlow<UiPropertyFormatsListState>(Hidden)
|
||||
val commands = MutableSharedFlow<Command>()
|
||||
|
||||
private val permission = MutableStateFlow(userPermissionProvider.get(vmParams.spaceId))
|
||||
|
||||
init {
|
||||
Timber.d("Space Properties ViewModel init")
|
||||
setupUIState()
|
||||
}
|
||||
|
||||
private fun setupUIState() {
|
||||
viewModelScope.launch {
|
||||
storeOfRelations.trackChanges()
|
||||
.collectLatest { event ->
|
||||
val allProperties = storeOfRelations.getAll().mapNotNull { property ->
|
||||
if (property.isHidden == true) {
|
||||
null
|
||||
} else {
|
||||
UiSpacePropertyItem(
|
||||
id = property.id,
|
||||
key = RelationKey(property.key),
|
||||
name = property.name.orEmpty(),
|
||||
format = property.format,
|
||||
isEditableField = fieldParser.isPropertyEditable(property),
|
||||
limitObjectTypes = storeOfObjectTypes.mapLimitObjectTypes(property = property)
|
||||
)
|
||||
}
|
||||
}.sortedBy { it.name }
|
||||
uiItemsState.value = UiSpacePropertiesScreenState(allProperties)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onBackClicked() {
|
||||
viewModelScope.launch {
|
||||
commands.emit(Command.Back)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateNewPropertyClicked() {
|
||||
if (permission.value?.isOwnerOrEditor() == true) {
|
||||
createNewProperty()
|
||||
} else {
|
||||
viewModelScope.launch {
|
||||
commands.emit(ShowToast("You don't have permission to create new properties"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNewProperty() {
|
||||
viewModelScope.launch {
|
||||
val format = DEFAULT_NEW_PROPERTY_FORMAT
|
||||
uiEditPropertyScreen.value = New(
|
||||
name = "",
|
||||
formatName = stringResourceProvider.getPropertiesFormatPrettyString(format),
|
||||
formatIcon = format.simpleIcon(),
|
||||
format = format,
|
||||
showLimitTypes = false,
|
||||
limitObjectTypes = format.getAllObjectTypesByFormat(storeOfObjectTypes)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onPropertyClicked(item: UiSpacePropertyItem) {
|
||||
viewModelScope.launch {
|
||||
val computedLimitTypes = computeLimitTypes(item = item)
|
||||
val formatName = stringResourceProvider.getPropertiesFormatPrettyString(item.format)
|
||||
val formatIcon = item.format.simpleIcon()
|
||||
uiEditPropertyScreen.value =
|
||||
if (permission.value?.isOwnerOrEditor() == true && item.isEditableField) {
|
||||
Edit(
|
||||
id = item.id,
|
||||
key = item.key.key,
|
||||
name = item.name,
|
||||
formatName = formatName,
|
||||
formatIcon = formatIcon,
|
||||
format = item.format,
|
||||
limitObjectTypes = computedLimitTypes,
|
||||
isPossibleToUnlinkFromType = false,
|
||||
showLimitTypes = false
|
||||
)
|
||||
} else {
|
||||
View(
|
||||
id = item.id,
|
||||
key = item.key.key,
|
||||
name = item.name,
|
||||
formatName = formatName,
|
||||
formatIcon = formatIcon,
|
||||
format = item.format,
|
||||
limitObjectTypes = computedLimitTypes,
|
||||
isPossibleToUnlinkFromType = false,
|
||||
showLimitTypes = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun computeLimitTypes(item: UiSpacePropertyItem): List<UiPropertyLimitTypeItem> {
|
||||
return item.limitObjectTypes.mapNotNull { id ->
|
||||
storeOfObjectTypes.get(id = id)?.let { objType ->
|
||||
UiPropertyLimitTypeItem(
|
||||
id = objType.id,
|
||||
name = fieldParser.getObjectName(objectWrapper = objType),
|
||||
icon = objType.objectIcon(),
|
||||
uniqueKey = objType.uniqueKey
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region Edit or Create Property
|
||||
fun onDismissPropertyScreen() {
|
||||
uiEditPropertyScreen.value = UiEditPropertyState.Hidden
|
||||
}
|
||||
|
||||
fun onEvent(event: SpacePropertiesEvent) {
|
||||
when (event) {
|
||||
is SpacePropertiesEvent.OnPropertyNameUpdate -> updatePropertyName(event.name)
|
||||
SpacePropertiesEvent.OnSaveButtonClicked -> saveProperty()
|
||||
SpacePropertiesEvent.OnLimitTypesClick -> toggleLimitTypes(show = true)
|
||||
SpacePropertiesEvent.OnLimitTypesDismiss -> toggleLimitTypes(show = false)
|
||||
is SpacePropertiesEvent.OnLimitTypesDoneClick -> applyLimitTypes(event.items)
|
||||
SpacePropertiesEvent.OnPropertyFormatClick -> showPropertyFormatsList()
|
||||
SpacePropertiesEvent.OnPropertyFormatsListDismiss -> {
|
||||
uiPropertyFormatsListState.value = Hidden
|
||||
}
|
||||
|
||||
is SpacePropertiesEvent.OnPropertyFormatSelected -> updatePropertyFormat(event.format)
|
||||
SpacePropertiesEvent.OnCreateNewButtonClicked -> proceedWithCreatingProperty()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePropertyName(newName: String) {
|
||||
val state = uiEditPropertyScreen.value as? UiEditPropertyState.Visible ?: return
|
||||
uiEditPropertyScreen.value = when (state) {
|
||||
is Edit -> state.copy(name = newName)
|
||||
is New -> state.copy(name = newName)
|
||||
is View -> state // Not editable
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveProperty() {
|
||||
val state = uiEditPropertyScreen.value as? 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("Property updated: $it")
|
||||
uiEditPropertyScreen.value = UiEditPropertyState.Hidden
|
||||
},
|
||||
onFailure = { error ->
|
||||
Timber.e(error, "Failed to update property")
|
||||
commands.emit(ShowToast("Failed to update property"))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleLimitTypes(show: Boolean) {
|
||||
uiEditPropertyScreen.value = when (val state = uiEditPropertyScreen.value) {
|
||||
is Edit -> state.copy(showLimitTypes = show)
|
||||
is New -> state.copy(showLimitTypes = show)
|
||||
is View -> state.copy(showLimitTypes = show)
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyLimitTypes(items: List<Id>) {
|
||||
val state = uiEditPropertyScreen.value as? New ?: run {
|
||||
Timber.w("Possible only for New state")
|
||||
return
|
||||
}
|
||||
uiEditPropertyScreen.value = state.copy(
|
||||
selectedLimitTypeIds = items,
|
||||
showLimitTypes = false
|
||||
)
|
||||
}
|
||||
|
||||
private fun showPropertyFormatsList() {
|
||||
uiEditPropertyScreen.value as? New ?: run {
|
||||
Timber.w("Possible only for New state")
|
||||
return
|
||||
}
|
||||
uiPropertyFormatsListState.value = Visible(
|
||||
items = UiEditTypePropertiesState.PROPERTIES_FORMATS.map { format ->
|
||||
Format(
|
||||
format = format,
|
||||
prettyName = stringResourceProvider.getPropertiesFormatPrettyString(format)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun updatePropertyFormat(selectedFormat: Format) {
|
||||
viewModelScope.launch {
|
||||
uiPropertyFormatsListState.value = Hidden
|
||||
val state = uiEditPropertyScreen.value as? UiEditPropertyState.Visible ?: return@launch
|
||||
uiEditPropertyScreen.value = when (state) {
|
||||
is New -> {
|
||||
val newFormat = selectedFormat.format
|
||||
state.copy(
|
||||
formatName = stringResourceProvider.getPropertiesFormatPrettyString(
|
||||
newFormat
|
||||
),
|
||||
formatIcon = newFormat.simpleIcon(),
|
||||
format = newFormat,
|
||||
limitObjectTypes = newFormat.getAllObjectTypesByFormat(storeOfObjectTypes),
|
||||
selectedLimitTypeIds = emptyList(),
|
||||
showLimitTypes = false
|
||||
)
|
||||
}
|
||||
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithCreatingProperty() {
|
||||
viewModelScope.launch {
|
||||
val state = uiEditPropertyScreen.value as? New ?: return@launch
|
||||
val params = CreateRelation.Params(
|
||||
space = vmParams.spaceId.id,
|
||||
format = state.format,
|
||||
name = state.name,
|
||||
limitObjectTypes = state.selectedLimitTypeIds,
|
||||
prefilled = emptyMap()
|
||||
)
|
||||
createRelation(params).process(
|
||||
success = { property ->
|
||||
Timber.d("Property created: $property")
|
||||
uiEditPropertyScreen.value = UiEditPropertyState.Hidden
|
||||
},
|
||||
failure = { error ->
|
||||
Timber.e(error, "Failed to create property")
|
||||
commands.emit(Command.ShowToast("Error while creating property"))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
sealed class Command {
|
||||
data object Back : Command()
|
||||
data class ShowToast(val message: String) : Command()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DEFAULT_NEW_PROPERTY_FORMAT = RelationFormat.LONG_TEXT
|
||||
}
|
||||
|
||||
data class VmParams(
|
||||
val spaceId: SpaceId
|
||||
)
|
||||
}
|
||||
|
||||
class SpacePropertiesVmFactory @Inject constructor(
|
||||
private val vmParams: SpacePropertiesViewModel.VmParams,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val analytics: Analytics,
|
||||
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
|
||||
private val userPermissionProvider: UserPermissionProvider,
|
||||
private val fieldParser: FieldParser,
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val createRelation: CreateRelation
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T =
|
||||
SpacePropertiesViewModel(
|
||||
vmParams = vmParams,
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
analytics = analytics,
|
||||
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
|
||||
userPermissionProvider = userPermissionProvider,
|
||||
fieldParser = fieldParser,
|
||||
storeOfRelations = storeOfRelations,
|
||||
stringResourceProvider = stringResourceProvider,
|
||||
setObjectDetails = setObjectDetails,
|
||||
createRelation = createRelation
|
||||
) as T
|
||||
}
|
||||
|
||||
data class UiSpacePropertiesScreenState(
|
||||
val items: List<UiSpacePropertyItem>
|
||||
) {
|
||||
companion object {
|
||||
val Empty = UiSpacePropertiesScreenState(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
data class UiSpacePropertyItem(
|
||||
val id: Id,
|
||||
val key: RelationKey,
|
||||
val name: String,
|
||||
val format: RelationFormat,
|
||||
val isEditableField: Boolean,
|
||||
val limitObjectTypes: List<Id>
|
||||
)
|
|
@ -0,0 +1,219 @@
|
|||
package com.anytypeio.anytype.feature_properties.space.ui
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
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.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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.PreviewTitle1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun SpacePropertiesListScreen(
|
||||
uiState: UiSpacePropertiesScreenState,
|
||||
onPropertyClicked: (UiSpacePropertyItem) -> Unit,
|
||||
onBackPressed: () -> Unit,
|
||||
onAddIconClicked: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = colorResource(id = R.color.background_primary))
|
||||
.systemBarsPadding()
|
||||
) {
|
||||
|
||||
Topbar(
|
||||
onBackPressed = onBackPressed,
|
||||
onAddIconClicked = onAddIconClicked
|
||||
)
|
||||
|
||||
val contentModifier =
|
||||
if (Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||
.fillMaxWidth()
|
||||
else
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
val items = remember {
|
||||
mutableStateListOf<UiSpacePropertyItem>()
|
||||
}
|
||||
items.swapList(uiState.items)
|
||||
|
||||
LazyColumn(
|
||||
modifier = contentModifier,
|
||||
state = lazyListState,
|
||||
) {
|
||||
items(
|
||||
count = items.size,
|
||||
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()
|
||||
}
|
||||
)
|
||||
item {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(32.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Relation(
|
||||
modifier: Modifier,
|
||||
item: UiSpacePropertyItem
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalAlignment = CenterVertically
|
||||
) {
|
||||
val icon = item.format.simpleIcon()
|
||||
PropertyIcon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
formatIconRes = icon
|
||||
)
|
||||
val name = item.name.trim().ifBlank { stringResource(R.string.untitled) }
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 10.dp),
|
||||
text = name,
|
||||
style = PreviewTitle1Regular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.PropertyIcon(
|
||||
modifier: Modifier,
|
||||
formatIconRes: Int?
|
||||
) {
|
||||
if (formatIconRes != null) {
|
||||
Image(
|
||||
painter = painterResource(id = formatIconRes),
|
||||
contentDescription = "Property format icon",
|
||||
contentScale = ContentScale.None,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun Topbar(
|
||||
onBackPressed: () -> Unit,
|
||||
onAddIconClicked: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets.statusBars)
|
||||
.height(48.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(56.dp)
|
||||
.fillMaxHeight()
|
||||
.noRippleThrottledClickable {
|
||||
onBackPressed()
|
||||
},
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp)
|
||||
.wrapContentSize(),
|
||||
painter = painterResource(R.drawable.ic_default_top_back),
|
||||
contentDescription = stringResource(R.string.content_desc_back_button)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = stringResource(R.string.space_properties_screen_title),
|
||||
style = Title1,
|
||||
color = colorResource(R.color.text_primary),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(56.dp)
|
||||
.height(48.dp)
|
||||
.align(Alignment.CenterEnd)
|
||||
.noRippleThrottledClickable {
|
||||
onAddIconClicked()
|
||||
},
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp)
|
||||
.wrapContentSize(),
|
||||
painter = painterResource(R.drawable.ic_default_plus),
|
||||
contentDescription = "Add new type"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.anytypeio.anytype.feature_properties.space.ui
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesItem.Format
|
||||
|
||||
sealed class SpacePropertiesEvent {
|
||||
|
||||
data class OnPropertyNameUpdate(val name: String) : SpacePropertiesEvent()
|
||||
data object OnSaveButtonClicked : SpacePropertiesEvent()
|
||||
data object OnCreateNewButtonClicked : SpacePropertiesEvent()
|
||||
|
||||
data object OnPropertyFormatClick : SpacePropertiesEvent()
|
||||
data object OnPropertyFormatsListDismiss : SpacePropertiesEvent()
|
||||
data class OnPropertyFormatSelected(val format: Format) : SpacePropertiesEvent()
|
||||
|
||||
data object OnLimitTypesClick : SpacePropertiesEvent()
|
||||
data object OnLimitTypesDismiss : SpacePropertiesEvent()
|
||||
data class OnLimitTypesDoneClick(val items: List<Id>) : SpacePropertiesEvent()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue