diff --git a/app/src/main/java/com/anytypeio/anytype/ui/primitives/ObjectTypeFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/primitives/ObjectTypeFragment.kt index 12cdb53745..00858a3fb7 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/primitives/ObjectTypeFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/primitives/ObjectTypeFragment.kt @@ -28,13 +28,12 @@ import com.anytypeio.anytype.core_utils.ext.subscribe import com.anytypeio.anytype.core_utils.ext.toast import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment import com.anytypeio.anytype.di.common.componentManager -import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeMainScreen -import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeVMFactory -import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeViewModel -import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeVmParams -import com.anytypeio.anytype.feature_object_type.ui.UiErrorState import com.anytypeio.anytype.feature_object_type.fields.ui.FieldsMainScreen import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeCommand +import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeVmParams +import com.anytypeio.anytype.feature_object_type.ui.UiErrorState +import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeVMFactory +import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeViewModel import com.anytypeio.anytype.presentation.home.OpenObjectNavigation import com.anytypeio.anytype.ui.chats.ChatFragment import com.anytypeio.anytype.ui.date.DateObjectFragment @@ -153,16 +152,20 @@ class ObjectTypeFragment : BaseComposeFragment() { startDestination = OBJ_TYPE_MAIN ) { composable(route = OBJ_TYPE_MAIN) { - ObjectTypeMainScreen( + WithSetScreen( + uiEditButtonState = vm.uiEditButtonState.collectAsStateWithLifecycle().value, uiSyncStatusBadgeState = vm.uiSyncStatusBadgeState.collectAsStateWithLifecycle().value, - uiSyncStatusState = vm.uiSyncStatusWidgetState.collectAsStateWithLifecycle().value, - uiTitleState = vm.uiTitleState.collectAsStateWithLifecycle().value, uiIconState = vm.uiIconState.collectAsStateWithLifecycle().value, + uiTitleState = vm.uiTitleState.collectAsStateWithLifecycle().value, uiFieldsButtonState = vm.uiFieldsButtonState.collectAsStateWithLifecycle().value, uiLayoutButtonState = vm.uiLayoutButtonState.collectAsStateWithLifecycle().value, - uiDeleteAlertState = vm.uiAlertState.collectAsStateWithLifecycle().value, - uiEditButtonState = vm.uiEditButtonState.collectAsStateWithLifecycle().value, + uiTemplatesButtonState = vm.uiTemplatesButtonState.collectAsStateWithLifecycle().value, + uiTemplatesModalListState = vm.uiTemplatesModalListState.collectAsStateWithLifecycle().value, uiLayoutTypeState = vm.uiTypeLayoutsState.collectAsStateWithLifecycle().value, + uiSyncStatusState = vm.uiSyncStatusWidgetState.collectAsStateWithLifecycle().value, + uiDeleteAlertState = vm.uiAlertState.collectAsStateWithLifecycle().value, + objectId = objectId, + space = space, onTypeEvent = vm::onTypeEvent ) } diff --git a/app/src/main/java/com/anytypeio/anytype/ui/primitives/WithSetScreen.kt b/app/src/main/java/com/anytypeio/anytype/ui/primitives/WithSetScreen.kt new file mode 100644 index 0000000000..a0d600e3e2 --- /dev/null +++ b/app/src/main/java/com/anytypeio/anytype/ui/primitives/WithSetScreen.kt @@ -0,0 +1,43 @@ +package com.anytypeio.anytype.ui.primitives + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import com.anytypeio.anytype.feature_object_type.ui.TypeEvent +import com.anytypeio.anytype.feature_object_type.ui.UiDeleteAlertState +import com.anytypeio.anytype.feature_object_type.ui.UiEditButton +import com.anytypeio.anytype.feature_object_type.ui.UiFieldsButtonState +import com.anytypeio.anytype.feature_object_type.ui.UiIconState +import com.anytypeio.anytype.feature_object_type.ui.UiLayoutButtonState +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.UiTemplatesButtonState +import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesModalListState +import com.anytypeio.anytype.feature_object_type.ui.UiTitleState +import com.anytypeio.anytype.presentation.sync.SyncStatusWidgetState + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun WithSetScreen( + //top bar + uiEditButtonState: UiEditButton, + uiSyncStatusBadgeState: UiSyncStatusBadgeState, + uiSyncStatusState: SyncStatusWidgetState, + //header + uiIconState: UiIconState, + uiTitleState: UiTitleState, + //layout and fields buttons + uiFieldsButtonState: UiFieldsButtonState, + uiLayoutButtonState: UiLayoutButtonState, + uiLayoutTypeState: UiLayoutTypeState, + uiTemplatesButtonState: UiTemplatesButtonState, + //templates modal list + uiTemplatesModalListState: UiTemplatesModalListState, + //delete alert + uiDeleteAlertState: UiDeleteAlertState, + //events + onTypeEvent: (TypeEvent) -> Unit, + objectId: String, + space: String, +) { + //next PR +} \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt index 992a4e076d..e725131526 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt @@ -705,6 +705,57 @@ open class ObjectSetFragment : dataViewInfo.hide() setViewer(viewer = null) } + is DataViewViewState.TypeSet.Default -> { + topToolbarThreeDotsButton.gone() + topToolbarStatusContainer.gone() + topBackButton.gone() + initView.gone() + header.gone() + dataViewHeader.visible() + viewerTitle.isEnabled = true + setupNewButtonsForTypeSet(state.isCreateObjectAllowed) + if (state.isEditingViewAllowed) { + customizeViewButton.visible() + } else { + customizeViewButton.invisible() + } + customizeViewButton.isEnabled = true + setCurrentViewerName(state.viewer?.title) + setViewer(viewer = state.viewer) + dataViewInfo.hide() + } + is DataViewViewState.TypeSet.NoItems -> { + topToolbarThreeDotsButton.gone() + topToolbarStatusContainer.gone() + topBackButton.gone() + initView.gone() + header.gone() + dataViewHeader.visible() + viewerTitle.isEnabled = true + setupNewButtonsForTypeSet(state.isCreateObjectAllowed) + if (state.isEditingViewAllowed) { + customizeViewButton.visible() + } else { + customizeViewButton.invisible() + } + customizeViewButton.isEnabled = true + setCurrentViewerName(state.title) + dataViewInfo.show( + type = DataViewInfo.TYPE.SET_NO_ITEMS, + isReadOnlyAccess = !state.isCreateObjectAllowed + ) + setViewer(viewer = null) + } + + is DataViewViewState.TypeSet.Error -> { + topToolbarThreeDotsButton.gone() + topToolbarStatusContainer.gone() + topBackButton.gone() + initView.gone() + header.gone() + dataViewHeader.visible() + setViewer(viewer = null) + } } } @@ -726,6 +777,16 @@ open class ObjectSetFragment : } } + private fun setupNewButtonsForTypeSet(isCreateObjectAllowed: Boolean) { + if (isCreateObjectAllowed) { + addNewButton.visible() + addNewIconButton.gone() + } else { + addNewButton.gone() + addNewIconButton.gone() + } + } + private fun setViewer(viewer: Viewer?) { when (viewer) { is Viewer.GridView -> { diff --git a/feature-object-type/build.gradle b/feature-object-type/build.gradle index 0ae1a94563..452eeca249 100644 --- a/feature-object-type/build.gradle +++ b/feature-object-type/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation libs.appcompat implementation libs.compose + implementation libs.fragmentCompose implementation libs.composeFoundation implementation libs.composeToolingPreview implementation libs.composeMaterial3 diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/TypeScreen.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/TypeScreen.kt index 4270eb664e..e892784412 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/TypeScreen.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/TypeScreen.kt @@ -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 ) { diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiEvent.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiEvent.kt index eea79c7d99..510f7a883b 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiEvent.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiEvent.kt @@ -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() } \ No newline at end of file diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiState.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiState.kt index fe23bffce0..aa54296eaf 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiState.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/UiState.kt @@ -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 + + data class Hidden( + override val items: List, + ) : UiTemplatesModalListState() { + companion object { + val EMPTY = Hidden(items = emptyList()) + } + } + + data class Visible( + override val items: List, + val showAddIcon: Boolean + ) : + UiTemplatesModalListState() +} //endregion //region OBJECTS HEADER diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/header/HorizontalButtons.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/header/HorizontalButtons.kt index df5f6bf2ad..d7aa576b0a 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/header/HorizontalButtons.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/header/HorizontalButtons.kt @@ -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 = {} ) } diff --git a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/layouts/LayoutsScreen.kt b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/layouts/LayoutsScreen.kt index a7cd2e4d9a..414c250e57 100644 --- a/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/layouts/LayoutsScreen.kt +++ b/feature-object-type/src/main/java/com/anytypeio/anytype/feature_object_type/ui/layouts/LayoutsScreen.kt @@ -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 ) { 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 82e58a481b..9970b56ee0 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 @@ -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.Companion.EMPTY) val uiIconState = MutableStateFlow(UiIconState.Companion.EMPTY) - //layout and fields buttons + //layout, fields and templates buttons val uiFieldsButtonState = MutableStateFlow(UiFieldsButtonState.Hidden) val uiLayoutButtonState = MutableStateFlow(UiLayoutButtonState.Hidden) + val uiTemplatesButtonState = MutableStateFlow(UiTemplatesButtonState.Hidden) //type layouts val uiTypeLayoutsState = MutableStateFlow(Hidden) - //templates header - val uiTemplatesHeaderState = - MutableStateFlow(UiTemplatesHeaderState.Hidden) - val uiTemplatesAddIconState = - MutableStateFlow(UiTemplatesAddIconState.Hidden) + //templates modal list state + val uiTemplatesModalListState = + MutableStateFlow(UiTemplatesModalListState.Hidden.EMPTY) //alerts val uiAlertState = MutableStateFlow(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, 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 { - 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 + ) + } + } } } diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml index 09ed77968f..ec29d9090d 100644 --- a/localization/src/main/res/values/strings.xml +++ b/localization/src/main/res/values/strings.xml @@ -790,6 +790,7 @@ Layout Fields Layout + Templates Restore from archive Icon Emoji or image for object @@ -1869,7 +1870,7 @@ Please provide specific details of your needs here. File Local fields Fields - You editing type + You\'re editing type Edit field New field Preview field @@ -1880,6 +1881,7 @@ Please provide specific details of your needs here. Local fields Some fields are not included in the object type. Please add them if you want to see them in all objects of this type, or remove them. Got it + This type doesn’t have\nany templates Error opening current object type @@ -1907,7 +1909,7 @@ Please provide specific details of your needs here. Probably someone has just removed the reaction or technical issue happened Human - Delete completely + Delete type It cannot be restored after confirmation Delete this message? diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt index aa0052eab3..cb133b6d32 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt @@ -559,7 +559,7 @@ fun MDVView.toCoreModels(): DVViewer = DVViewer( coverFit = coverFit, coverRelationKey = coverRelationKey.ifEmpty { null }, defaultTemplate = defaultTemplateId.ifEmpty { null }, - defaultObjectType = defaultObjectTypeId.ifEmpty { ObjectTypeIds.PAGE } + defaultObjectType = defaultObjectTypeId.ifEmpty { null } ) fun MDVRelation.toCoreModels(): DVViewerRelation = DVViewerRelation( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/AnalyticsExt.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/AnalyticsExt.kt index 91d9a89f2b..f7867af857 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/AnalyticsExt.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/AnalyticsExt.kt @@ -1028,6 +1028,10 @@ fun CoroutineScope.logEvent( is ObjectState.DataView.Set -> { Pair("ot-set", state.viewerByIdOrFirst(currentViewId)?.type?.formattedName) } + + is ObjectState.DataView.TypeSet -> { + Pair("ot-set", state.viewerByIdOrFirst(currentViewId)?.type?.formattedName) + } } val scope = this when (event) { @@ -1054,6 +1058,18 @@ fun CoroutineScope.logEvent( spaceParams = spaceParams ) ) + + is ObjectState.DataView.TypeSet -> scope.sendEvent( + analytics = analytics, + eventName = setScreenShow, + startTime = startTime, + middleTime = middleTime, + props = buildProps( + embedType = embedTypeDefault, + type = viewerType, + spaceParams = spaceParams + ) + ) } } ObjectStateAnalyticsEvent.TURN_INTO_COLLECTION -> { @@ -1244,7 +1260,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.OBJECT_CREATE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, @@ -1262,7 +1278,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.SELECT_TEMPLATE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, @@ -1278,7 +1294,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.SHOW_TEMPLATES -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, @@ -1294,7 +1310,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.CREATE_TEMPLATE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, @@ -1312,7 +1328,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.EDIT_TEMPLATE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, @@ -1330,7 +1346,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.DUPLICATE_TEMPLATE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, @@ -1348,7 +1364,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.DELETE_TEMPLATE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, @@ -1366,7 +1382,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.SET_AS_DEFAULT_TYPE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } if (state.isChangingDefaultTypeAvailable()) { scope.sendEvent( @@ -1386,7 +1402,7 @@ fun CoroutineScope.logEvent( ObjectStateAnalyticsEvent.CHANGE_DEFAULT_TEMPLATE -> { val route = when (state) { is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection - is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet + is ObjectState.DataView.Set, is ObjectState.DataView.TypeSet -> EventsDictionary.Routes.objCreateSet } scope.sendEvent( analytics = analytics, diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectRelationProvider.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectRelationProvider.kt index 1f516555df..4e4649d03b 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectRelationProvider.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectRelationProvider.kt @@ -62,6 +62,14 @@ class DataViewObjectRelationProvider( } } } + is ObjectState.DataView.TypeSet -> { + val objectKeys = state.dataViewContent.relationLinks.map { it.key } + flow { + objectKeys.mapNotNull { + storeOfRelations.getByKey(it) + } + } + } else -> emptyFlow() } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectValueProvider.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectValueProvider.kt index d75d348140..bcaa395ba7 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectValueProvider.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/DataViewObjectValueProvider.kt @@ -46,6 +46,7 @@ class DataViewObjectValueProvider( ) : Struct = when (state) { is ObjectState.DataView.Collection -> state.details.getStruct(target).orEmpty() is ObjectState.DataView.Set -> state.details.getStruct(target).orEmpty() + is ObjectState.DataView.TypeSet -> state.details.getStruct(target).orEmpty() else -> emptyMap() } } @@ -81,6 +82,7 @@ class SetOrCollectionObjectValueProvider( ) : Struct = when (state) { is ObjectState.DataView.Collection -> state.details.getStruct(target).orEmpty() is ObjectState.DataView.Set -> state.details.getStruct(target).orEmpty() + is ObjectState.DataView.TypeSet -> state.details.getStruct(target).orEmpty() else -> emptyMap() } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/ObjectRelationListProvider.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/ObjectRelationListProvider.kt index bd14934686..55ff640fd5 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/ObjectRelationListProvider.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/providers/ObjectRelationListProvider.kt @@ -41,6 +41,10 @@ interface ObjectRelationListProvider { state.details } + is ObjectState.DataView.TypeSet -> { + state.details + } + else -> ObjectViewDetails.EMPTY } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/DataViewViewState.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/DataViewViewState.kt index d930fae01c..fa0f031227 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/DataViewViewState.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/DataViewViewState.kt @@ -55,6 +55,30 @@ sealed class DataViewViewState { ) : Set() } + sealed class TypeSet : DataViewViewState() { + + abstract val isCreateObjectAllowed: Boolean + abstract val isEditingViewAllowed: Boolean + + data class NoItems( + val title: String, + override val isCreateObjectAllowed: Boolean = true, + override val isEditingViewAllowed: Boolean = false + ) : TypeSet() + + data class Default( + val viewer: Viewer?, + override val isCreateObjectAllowed: Boolean = true, + override val isEditingViewAllowed: Boolean = false + ) : TypeSet() + + data class Error( + val msg: String, + override val isCreateObjectAllowed: Boolean = false, + override val isEditingViewAllowed: Boolean = false + ) : TypeSet() + } + object Init: DataViewViewState() data class Error(val msg: String) : DataViewViewState() } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetCreateBookmarkRecordViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetCreateBookmarkRecordViewModel.kt index c90c842b29..868223dd77 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetCreateBookmarkRecordViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetCreateBookmarkRecordViewModel.kt @@ -92,6 +92,19 @@ class ObjectSetCreateBookmarkRecordViewModel( ) { isCompleted.value = true } } } + + is ObjectState.DataView.TypeSet -> { + val viewer = state.viewerByIdOrFirst(session.currentViewerId.value) ?: return + val prefilled = viewer.prefillNewObjectDetails( + dateProvider = dateProvider, + storeOfRelations = storeOfRelations, + dataViewRelationLinks = state.dataViewContent.relationLinks + ) + createBookmark( + input = input, + details = prefilled + ) { isCompleted.value = true } + } } } else { sendToast("Url is invalid.") diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt index 63bd8d1fa9..40f78907ec 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt @@ -397,6 +397,10 @@ fun ObjectState.DataView.Set.getSetOfValue(ctx: Id): List { return details.getObject(ctx)?.setOf.orEmpty() } +fun ObjectState.DataView.TypeSet.getSetOfValue(ctx: Id): List { + return details.getObject(ctx)?.setOf.orEmpty() +} + fun ObjectState.DataView.filterOutDeletedAndMissingObjects(query: List): List { return query.filter(::isValidObject) } @@ -488,6 +492,16 @@ suspend fun ObjectState.DataView.toViewersView(ctx: Id, session: ObjectSetSessio ) } } + + is ObjectState.DataView.TypeSet -> { + val setOfValue = getSetOfValue(ctx) + mapViewers( + defaultObjectType = { setOfValue.firstOrNull() }, + viewers = viewers, + session = session, + storeOfRelations = storeOfRelations + ) + } } } @@ -551,6 +565,23 @@ suspend fun ObjectState.DataView.getActiveViewTypeAndTemplate( } } } + + is ObjectState.DataView.TypeSet -> { + val setOfValue = getSetOfValue(ctx) + val setOf = setOfValue.firstOrNull() + return if (setOf.isNullOrBlank()) { + Timber.d("Set by type setOf param is null or empty, not possible to get Type and Template") + Pair(null, null) + } else { + val defaultSetObjectType = details.getTypeObject(setOf) + if (activeView.defaultTemplate.isNullOrEmpty()) { + val defaultTemplateId = defaultSetObjectType?.defaultTemplateId + Pair(defaultSetObjectType, defaultTemplateId) + } else { + Pair(defaultSetObjectType, activeView.defaultTemplate) + } + } + } } } @@ -580,6 +611,7 @@ fun ObjectState.DataView.isChangingDefaultTypeAvailable(): Boolean { val setOfValue = getSetOfValue(root) isSetByRelation(setOfValue = setOfValue) } + is ObjectState.DataView.TypeSet -> false } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt index 33638fb263..a0bc07aec4 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt @@ -87,6 +87,7 @@ import com.anytypeio.anytype.presentation.relations.ObjectSetConfig.DEFAULT_LIMI import com.anytypeio.anytype.presentation.relations.RelationListViewModel import com.anytypeio.anytype.presentation.relations.render import com.anytypeio.anytype.presentation.search.ObjectSearchConstants +import com.anytypeio.anytype.presentation.sets.ObjectSetCommand.Modal.* import com.anytypeio.anytype.presentation.sets.model.CellView import com.anytypeio.anytype.presentation.sets.model.Viewer import com.anytypeio.anytype.presentation.sets.state.ObjectState @@ -479,6 +480,22 @@ class ObjectSetViewModel( } } + is ObjectState.DataView.TypeSet -> { + Timber.d("subscribeToObjectState, NEW TYPE SET STATE") + if (query.state.isInitialized) { + dataViewSubscription.startObjectTypeSetSubscription( + space = vmParams.space.id, + context = vmParams.ctx, + state = query.state, + currentViewerId = query.currentViewerId, + offset = query.offset, + dataViewRelationLinks = query.state.dataViewContent.relationLinks + ) + } else { + emptyFlow() + } + } + else -> { Timber.d("subscribeToObjectState, NEW STATE, ${query.state}") emptyFlow() @@ -584,6 +601,12 @@ class ObjectSetViewModel( ) ObjectState.Init -> DataViewViewState.Init ObjectState.ErrorLayout -> DataViewViewState.Error(msg = "Wrong layout, couldn't open object") + is ObjectState.DataView.TypeSet -> processTypeSetState( + dataViewState = dataViewState, + objectState = objectState, + currentViewId = currentViewId, + permission = permission + ) } } @@ -726,6 +749,75 @@ class ObjectSetViewModel( } } + private suspend fun processTypeSetState( + dataViewState: DataViewState, + objectState: ObjectState.DataView.TypeSet, + currentViewId: String?, + permission: SpaceMemberPermissions? + ): DataViewViewState { + if (!objectState.isInitialized) return DataViewViewState.Init + + val setOfValue = objectState.getSetOfValue(ctx = vmParams.ctx) + val query = objectState.filterOutDeletedAndMissingObjects(query = setOfValue) + val viewer = objectState.viewerByIdOrFirst(currentViewId) + + return when (dataViewState) { + DataViewState.Init -> { + _dvViews.value = emptyList() + when { + setOfValue.isEmpty() || query.isEmpty() || viewer == null -> + DataViewViewState.TypeSet.Error(msg = "Error while rendering viewer") + else -> DataViewViewState.Init + } + } + is DataViewState.Loaded -> { + _dvViews.value = objectState.dataViewState()?.toViewersView( + ctx = vmParams.ctx, + session = session, + storeOfRelations = storeOfRelations + ) ?: emptyList() + val relations = objectState.dataViewContent.relationLinks.mapNotNull { + storeOfRelations.getByKey(it.key) + } + val render = viewer?.render( + coverImageHashProvider = coverImageHashProvider, + builder = urlBuilder, + objects = dataViewState.objects, + dataViewRelations = relations, + store = objectStore, + storeOfRelations = storeOfRelations, + fieldParser = fieldParser + ) + + when { + render == null || query.isEmpty() || setOfValue.isEmpty() -> DataViewViewState.TypeSet.Error( + msg = "Error while rendering viewer", + ) + render.isEmpty() -> { + val (defType, _) = objectState.getActiveViewTypeAndTemplate( + vmParams.ctx, viewer, storeOfObjectTypes + ) + DataViewViewState.TypeSet.NoItems( + title = render.title, + isCreateObjectAllowed = objectState.isCreateObjectAllowed(defType) && (permission?.isOwnerOrEditor() == true), + isEditingViewAllowed = permission?.isOwnerOrEditor() == true + ) + } + else -> { + val (defType, _) = objectState.getActiveViewTypeAndTemplate( + vmParams.ctx, viewer, storeOfObjectTypes + ) + DataViewViewState.TypeSet.Default( + viewer = render, + isCreateObjectAllowed = objectState.isCreateObjectAllowed(defType) && (permission?.isOwnerOrEditor() == true), + isEditingViewAllowed = permission?.isOwnerOrEditor() == true + ) + } + } + } + } + } + private suspend fun renderViewer( objectState: ObjectState.DataView.Collection, dataViewState: DataViewState.Loaded, @@ -1015,7 +1107,7 @@ class ObjectSetViewModel( } // TODO Multispaces refactor this method - private suspend fun proceedWithCreatingSetObject(currentState: ObjectState.DataView.Set, templateChosenBy: Id?) { + private suspend fun proceedWithCreatingSetObject(currentState: ObjectState.DataView, templateChosenBy: Id?) { if (isRestrictionPresent(DataViewRestriction.CREATE_OBJECT)) { toast(NOT_ALLOWED) } else { @@ -1687,6 +1779,10 @@ class ObjectSetViewModel( dispatch(ObjectSetCommand.ShowOnlyAccessError) } } + + is ObjectState.DataView.TypeSet -> { + //do nothing + } } } else -> { @@ -2687,6 +2783,13 @@ class ObjectSetViewModel( templateChosenBy = templateId ) } + + is ObjectState.DataView.TypeSet -> { + proceedWithCreatingSetObject( + currentState = state, + templateChosenBy = templateId + ) + } } } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/DefaultObjectStateReducer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/DefaultObjectStateReducer.kt index f633faeb17..f4f1b0f671 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/DefaultObjectStateReducer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/DefaultObjectStateReducer.kt @@ -133,6 +133,12 @@ class DefaultObjectStateReducer : ObjectStateReducer { details = ObjectViewDetails(event.details), dataViewRestrictions = event.dataViewRestrictions ) + ObjectType.Layout.OBJECT_TYPE.code -> ObjectState.DataView.TypeSet( + root = event.root, + blocks = event.blocks, + details = ObjectViewDetails(event.details), + dataViewRestrictions = event.dataViewRestrictions + ) else -> { Timber.e("Wrong layout type: $layout") ObjectState.ErrorLayout @@ -157,6 +163,10 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = event.target, blockContentUpdate = updateBlockContent ) + is ObjectState.DataView.TypeSet -> state.updateBlockContent( + target = event.target, + blockContentUpdate = updateBlockContent + ) else -> state } } @@ -184,6 +194,10 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = event.target, blockContentUpdate = updateBlockContent ) + is ObjectState.DataView.TypeSet -> state.updateBlockContent( + target = event.target, + blockContentUpdate = updateBlockContent + ) else -> state } } @@ -211,6 +225,10 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = event.dv, blockContentUpdate = updateBlockContent ) + is ObjectState.DataView.TypeSet -> state.updateBlockContent( + target = event.dv, + blockContentUpdate = updateBlockContent + ) else -> state } } @@ -236,6 +254,10 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = event.dv, blockContentUpdate = updateBlockContent ) + is ObjectState.DataView.TypeSet -> state.updateBlockContent( + target = event.dv, + blockContentUpdate = updateBlockContent + ) else -> state } } @@ -261,6 +283,10 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = event.dv, blockContentUpdate = updateBlockContent ) + is ObjectState.DataView.TypeSet -> state.updateBlockContent( + target = event.dv, + blockContentUpdate = updateBlockContent + ) else -> state } } @@ -284,6 +310,10 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = event.dv, blockContentUpdate = updateBlockContent ) + is ObjectState.DataView.TypeSet -> state.updateBlockContent( + target = event.dv, + blockContentUpdate = updateBlockContent + ) else -> state } } @@ -360,6 +390,10 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = event.block, blockContentUpdate = updateBlockContent ) + is ObjectState.DataView.TypeSet -> state.updateBlockContent( + target = event.block, + blockContentUpdate = updateBlockContent + ) else -> state } } @@ -390,6 +424,15 @@ class DefaultObjectStateReducer : ObjectStateReducer { ) ) } + is ObjectState.DataView.TypeSet -> { + state.copy( + details = ObjectViewDetails( + details = state.details.details.toMutableMap().apply { + put(event.target, event.details) + } + ) + ) + } else -> state } } @@ -418,6 +461,14 @@ class DefaultObjectStateReducer : ObjectStateReducer { ) ) ) + is ObjectState.DataView.TypeSet -> state.copy( + details = state.details.copy( + details = state.details.details.amend( + target = event.target, + slice = event.details + ) + ) + ) else -> state } } @@ -446,6 +497,14 @@ class DefaultObjectStateReducer : ObjectStateReducer { ) ) ) + is ObjectState.DataView.TypeSet -> state.copy( + details = state.details.copy( + details = state.details.details.unset( + target = event.target, + keys = event.keys + ) + ) + ) else -> state } } @@ -470,6 +529,12 @@ class DefaultObjectStateReducer : ObjectStateReducer { target = { block -> block.id == event.id } ) ) + is ObjectState.DataView.TypeSet -> state.copy( + blocks = state.blocks.replace( + replacement = { target -> target.copy(children = event.children) }, + target = { block -> block.id == event.id } + ) + ) else -> state } } @@ -481,6 +546,7 @@ class DefaultObjectStateReducer : ObjectStateReducer { return when (state) { is ObjectState.DataView.Collection -> state.copy(blocks = state.blocks + event.blocks) is ObjectState.DataView.Set -> state.copy(blocks = state.blocks + event.blocks) + is ObjectState.DataView.TypeSet -> state.copy(blocks = state.blocks + event.blocks) ObjectState.Init -> state ObjectState.ErrorLayout -> state } @@ -502,6 +568,7 @@ class DefaultObjectStateReducer : ObjectStateReducer { return when (this) { is ObjectState.DataView.Collection -> copy(blocks = updatedBlocks) is ObjectState.DataView.Set -> copy(blocks = updatedBlocks) + is ObjectState.DataView.TypeSet -> copy(blocks = updatedBlocks) } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt index 1006df5a87..9e0c0c0b91 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/state/ObjectState.kt @@ -53,6 +53,20 @@ sealed class ObjectState { override val dataViewContent get() = dataViewBlock.content as DV override val viewers get() = dataViewContent.viewers } + + data class TypeSet( + override val root: Id, + override val blocks: List = emptyList(), + override val details: ObjectViewDetails = ObjectViewDetails.EMPTY, + override val objectRestrictions: List = emptyList(), + override val dataViewRestrictions: List = emptyList(), + ) : DataView() { + + override val isInitialized get() = blocks.any { it.content is DV } + override val dataViewBlock get() = blocks.first { it.content is DV } + override val dataViewContent get() = dataViewBlock.content as DV + override val viewers get() = dataViewContent.viewers + } } object Init : ObjectState() { diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/subscription/DataViewSubscription.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/subscription/DataViewSubscription.kt index d06499d7c9..167f75abc9 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/subscription/DataViewSubscription.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/subscription/DataViewSubscription.kt @@ -40,6 +40,15 @@ interface DataViewSubscription { dataViewRelationLinks: List ): Flow + suspend fun startObjectTypeSetSubscription( + context: Id, + space: Id, + state: ObjectState.DataView.TypeSet, + currentViewerId: Id?, + offset: Long, + dataViewRelationLinks: List + ): Flow + suspend fun unsubscribe(ids: List) } @@ -139,6 +148,59 @@ class DefaultDataViewSubscription( return dataViewSubscriptionContainer.observe(params) } + override suspend fun startObjectTypeSetSubscription( + context: Id, + space: Id, + state: ObjectState.DataView.TypeSet, + currentViewerId: Id?, + offset: Long, + dataViewRelationLinks: List + ): Flow { + if (context.isEmpty()) { + Timber.w("Data view TypeSet subscription: context is empty") + return emptyFlow() + } + val activeViewer = state.viewerByIdOrFirst(currentViewerId) + if (activeViewer == null) { + Timber.w("Data view TypeSet subscription: active viewer is null") + return emptyFlow() + } + + val setOfValue = state.getSetOfValue(ctx = context) + if (setOfValue.isEmpty()) { + Timber.w("Data view TypeSet subscription: setOf value is empty, proceed without subscription") + return emptyFlow() + } + + val query = state.filterOutDeletedAndMissingObjects(setOfValue) + if (query.isEmpty()) { + Timber.w( + "Data view TypeSet subscription: query has no valid types or relations, " + + "proceed without subscription" + ) + return emptyFlow() + } + + val filters = buildList { + addAll(activeViewer.filters.updateFormatForSubscription(relationLinks = dataViewRelationLinks)) + addAll(defaultDataViewFilters()) + } + val dataViewLinksKeys = state.dataViewContent.relationLinks.map { it.key } + val keys = ObjectSearchConstants.defaultDataViewKeys + dataViewLinksKeys + + val params = DataViewSubscriptionContainer.Params( + space = SpaceId(space), + subscription = getDataViewSubscriptionId(context), + sorts = activeViewer.sorts.updateWithRelationFormat(relationLinks = dataViewRelationLinks), + filters = filters, + sources = query, + keys = keys, + limit = ObjectSetConfig.DEFAULT_LIMIT, + offset = offset + ) + return dataViewSubscriptionContainer.observe(params) + } + override suspend fun unsubscribe(ids: List) { dataViewSubscriptionContainer.unsubscribe(ids) }