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

DROID-3435 Primitives | Type page as Set (#2134)

This commit is contained in:
Konstantin Ivanov 2025-03-06 22:00:01 +01:00 committed by GitHub
parent 51ffabc160
commit f179d64231
Signed by: github
GPG key ID: B5690EEEBB952194
23 changed files with 582 additions and 62 deletions

View file

@ -36,6 +36,7 @@ dependencies {
implementation libs.appcompat
implementation libs.compose
implementation libs.fragmentCompose
implementation libs.composeFoundation
implementation libs.composeToolingPreview
implementation libs.composeMaterial3

View file

@ -168,7 +168,7 @@ private fun MainContent(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBarContent(
fun TopBarContent(
uiSyncStatusBadgeState: UiSyncStatusBadgeState,
uiEditButtonState: UiEditButton,
uiTitleState: UiTitleState,
@ -199,7 +199,7 @@ private fun TopBarContent(
}
@Composable
private fun BottomSyncStatus(
fun BottomSyncStatus(
uiSyncStatusState: SyncStatusWidgetState,
onDismiss: () -> Unit
) {

View file

@ -21,6 +21,7 @@ sealed class TypeEvent {
//endregion
//region Templates
data object OnTemplatesModalListDismiss : TypeEvent()
data object OnTemplatesAddIconClick : TypeEvent()
data class OnTemplateItemClick(val item: TemplateView) : TypeEvent()
sealed class OnTemplateMenuClick : TypeEvent() {
@ -29,6 +30,7 @@ sealed class TypeEvent {
data class Duplicate(val item: TemplateView) : OnTemplateMenuClick()
data class Delete(val item: TemplateView) : OnTemplateMenuClick()
}
//endregion
//region Layout type
@ -39,4 +41,5 @@ sealed class TypeEvent {
data object OnLayoutButtonClick : TypeEvent()
data object OnFieldsButtonClick : TypeEvent()
data object OnTemplatesButtonClick : TypeEvent()
}

View file

@ -33,7 +33,8 @@ sealed class ObjectTypeCommand {
data object OpenFieldsScreen : ObjectTypeCommand()
data class OpenAddFieldScreen(val typeId: Id, val space: Id, val isSet: Boolean = false) : ObjectTypeCommand()
data class OpenAddFieldScreen(val typeId: Id, val space: Id, val isSet: Boolean = false) :
ObjectTypeCommand()
}
//region OBJECT TYPE HEADER (title + icon)
@ -71,6 +72,11 @@ sealed class UiFieldsButtonState {
}
sealed class UiTemplatesButtonState {
data object Hidden : UiTemplatesButtonState()
data class Visible(val count: Int) : UiTemplatesButtonState()
}
//region MENU
@Immutable
sealed class UiMenuSetItem {
@ -153,6 +159,24 @@ data class UiTemplatesListState(
val EMPTY = UiTemplatesListState(items = emptyList())
}
}
sealed class UiTemplatesModalListState {
abstract val items: List<TemplateView>
data class Hidden(
override val items: List<TemplateView>,
) : UiTemplatesModalListState() {
companion object {
val EMPTY = Hidden(items = emptyList())
}
}
data class Visible(
override val items: List<TemplateView>,
val showAddIcon: Boolean
) :
UiTemplatesModalListState()
}
//endregion
//region OBJECTS HEADER

View file

@ -1,6 +1,7 @@
package com.anytypeio.anytype.feature_object_type.ui.header
import androidx.compose.foundation.border
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@ -8,6 +9,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@ -21,21 +23,26 @@ import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.common.OldDevicesPreview
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
import com.anytypeio.anytype.feature_object_type.ui.TypeEvent
import com.anytypeio.anytype.feature_object_type.ui.UiFieldsButtonState
import com.anytypeio.anytype.feature_object_type.ui.UiLayoutButtonState
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesButtonState
@Composable
fun HorizontalButtons(
modifier: Modifier,
uiLayoutButtonState: UiLayoutButtonState,
uiFieldsButtonState: UiFieldsButtonState,
uiTemplatesButtonState: UiTemplatesButtonState = UiTemplatesButtonState.Hidden,
onTypeEvent: (TypeEvent) -> Unit
) {
val horizontalScrollState = rememberScrollState()
Row(
modifier = modifier,
modifier = modifier.horizontalScroll(state = horizontalScrollState),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
val modifierButton = Modifier
@ -99,6 +106,32 @@ fun HorizontalButtons(
)
}
}
if (uiTemplatesButtonState is UiTemplatesButtonState.Visible) {
Row(
modifier = modifierButton.noRippleThrottledClickable {
onTypeEvent(TypeEvent.OnTemplatesButtonClick)
},
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier
.wrapContentSize()
.padding(start = 12.dp),
text = stringResource(R.string.button_templates),
style = PreviewTitle2Medium,
color = colorResource(R.color.text_primary)
)
Text(
modifier = Modifier
.wrapContentSize()
.padding(start = 6.dp, end = 12.dp),
text = uiTemplatesButtonState.count.toString(),
style = PreviewTitle2Medium,
color = colorResource(R.color.glyph_active)
)
}
}
}
}
@ -112,6 +145,7 @@ fun HorizontalButtonsPreview() {
.padding(start = 20.dp),
uiLayoutButtonState = UiLayoutButtonState.Visible(ObjectType.Layout.BASIC),
uiFieldsButtonState = UiFieldsButtonState.Visible(3),
uiTemplatesButtonState = UiTemplatesButtonState.Visible(2),
onTypeEvent = {}
)
}

View file

@ -109,7 +109,7 @@ fun TypeLayoutsScreen(
modifier = Modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
TemplateItemContent(
LayoutItemContent(
modifier = Modifier
.width(120.dp)
.height(224.dp)
@ -147,7 +147,7 @@ fun TypeLayoutsScreen(
}
@Composable
private fun TemplateItemContent(
private fun LayoutItemContent(
modifier: Modifier,
item: ObjectType.Layout
) {

View file

@ -10,8 +10,6 @@ import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.permissions.ObjectPermissions
import com.anytypeio.anytype.core_models.permissions.toObjectPermissionsForTypes
import com.anytypeio.anytype.core_models.primitives.TypeId
import com.anytypeio.anytype.core_models.primitives.TypeKey
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider
@ -53,7 +51,8 @@ import com.anytypeio.anytype.feature_object_type.ui.UiLayoutTypeState
import com.anytypeio.anytype.feature_object_type.ui.UiLayoutTypeState.*
import com.anytypeio.anytype.feature_object_type.ui.UiSyncStatusBadgeState
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesAddIconState
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesHeaderState
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesButtonState
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesModalListState
import com.anytypeio.anytype.feature_object_type.ui.UiTitleState
import com.anytypeio.anytype.feature_object_type.ui.buildUiFieldsList
import com.anytypeio.anytype.feature_object_type.ui.mapToUiAddFieldListItem
@ -97,7 +96,7 @@ import timber.log.Timber
* Models: @see [ObjectViewState]
*/
class ObjectTypeViewModel(
private val vmParams: ObjectTypeVmParams,
val vmParams: ObjectTypeVmParams,
private val analytics: Analytics,
private val urlBuilder: UrlBuilder,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
@ -132,18 +131,17 @@ class ObjectTypeViewModel(
val uiTitleState = MutableStateFlow<UiTitleState>(UiTitleState.Companion.EMPTY)
val uiIconState = MutableStateFlow<UiIconState>(UiIconState.Companion.EMPTY)
//layout and fields buttons
//layout, fields and templates buttons
val uiFieldsButtonState = MutableStateFlow<UiFieldsButtonState>(UiFieldsButtonState.Hidden)
val uiLayoutButtonState = MutableStateFlow<UiLayoutButtonState>(UiLayoutButtonState.Hidden)
val uiTemplatesButtonState = MutableStateFlow<UiTemplatesButtonState>(UiTemplatesButtonState.Hidden)
//type layouts
val uiTypeLayoutsState = MutableStateFlow<UiLayoutTypeState>(Hidden)
//templates header
val uiTemplatesHeaderState =
MutableStateFlow<UiTemplatesHeaderState>(UiTemplatesHeaderState.Hidden)
val uiTemplatesAddIconState =
MutableStateFlow<UiTemplatesAddIconState>(UiTemplatesAddIconState.Hidden)
//templates modal list state
val uiTemplatesModalListState =
MutableStateFlow<UiTemplatesModalListState>(UiTemplatesModalListState.Hidden.EMPTY)
//alerts
val uiAlertState = MutableStateFlow<UiDeleteAlertState>(UiDeleteAlertState.Hidden)
@ -326,10 +324,6 @@ class ObjectTypeViewModel(
_objTypeState.value = objType
_objectTypePermissionsState.value = objectPermissions
if (!objectPermissions.canCreateTemplatesForThisType) {
uiTemplatesHeaderState.value = UiTemplatesHeaderState.Hidden
uiTemplatesAddIconState.value = UiTemplatesAddIconState.Hidden
}
uiTitleState.value = UiTitleState(
title = objType.name.orEmpty(),
isEditable = objectPermissions.canEditDetails
@ -368,31 +362,25 @@ class ObjectTypeViewModel(
templates: List<TemplateView>,
permissions: ObjectPermissions
) {
uiTemplatesHeaderState.value = UiTemplatesHeaderState.Visible(count = "${templates.size}")
uiTemplatesButtonState.value = UiTemplatesButtonState.Visible(count = templates.size)
// Update each template view regarding default selection.
val updatedTemplates = templates.map { template ->
when (template) {
is TemplateView.Blank -> template
is TemplateView.New -> template
is TemplateView.Template -> template.copy(
val updatedTemplates = templates.mapNotNull { template ->
if (template is TemplateView.Template) {
template.copy(
isDefault = template.id == objType.defaultTemplateId
)
} else {
null
}
}
// Build final list with an extra "new template" item if allowed.
val finalTemplates = buildList<TemplateView> {
addAll(updatedTemplates)
if (permissions.participantCanEdit) {
add(
TemplateView.New(
targetTypeId = TypeId(objType.id),
targetTypeKey = TypeKey(objType.uniqueKey)
)
)
uiTemplatesAddIconState.value = UiTemplatesAddIconState.Visible
}
val currentValue = uiTemplatesModalListState.value
uiTemplatesModalListState.value = when (currentValue) {
is UiTemplatesModalListState.Hidden -> currentValue.copy(updatedTemplates)
is UiTemplatesModalListState.Visible -> currentValue.copy(
updatedTemplates,
showAddIcon = permissions.canCreateTemplatesForThisType
)
}
}
@ -478,6 +466,22 @@ class ObjectTypeViewModel(
}
is TypeEvent.OnTemplateMenuClick -> proceedWithTemplateMenuClick(event)
TypeEvent.OnTemplatesModalListDismiss -> {
uiTemplatesModalListState.value = UiTemplatesModalListState.Hidden(
items = uiTemplatesModalListState.value.items
)
}
TypeEvent.OnTemplatesButtonClick -> {
viewModelScope.launch {
val currentState = uiTemplatesModalListState.value
uiTemplatesModalListState.value = UiTemplatesModalListState.Visible(
items = currentState.items,
showAddIcon = _objectTypePermissionsState.value?.canCreateTemplatesForThisType == true
)
}
}
}
}