mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-1432 Template | Enhancement | Flow in Sets/Collections (#266)
This commit is contained in:
parent
10a8cc89fe
commit
d7c0fea015
47 changed files with 1514 additions and 161 deletions
|
@ -127,6 +127,9 @@ abstract class TestObjectSetSetup {
|
|||
@Mock
|
||||
lateinit var storeOfObjectTypes: StoreOfObjectTypes
|
||||
|
||||
@Mock
|
||||
lateinit var getDefaultType: GetDefaultPageType
|
||||
|
||||
private lateinit var getTemplates: GetTemplates
|
||||
private lateinit var getDefaultPageType: GetDefaultPageType
|
||||
|
||||
|
@ -242,7 +245,9 @@ abstract class TestObjectSetSetup {
|
|||
setQueryToObjectSet = setQueryToObjectSet,
|
||||
objectStore = objectStore,
|
||||
addObjectToCollection = addObjectToCollection,
|
||||
storeOfObjectTypes = storeOfObjectTypes
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
getDefaultPageType = getDefaultPageType,
|
||||
getTemplates = getTemplates
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -195,7 +195,9 @@ object ObjectSetModule {
|
|||
@Named("object-set-store") objectStore: ObjectStore,
|
||||
addObjectToCollection: AddObjectToCollection,
|
||||
convertObjectToCollection: ConvertObjectToCollection,
|
||||
storeOfObjectTypes: StoreOfObjectTypes
|
||||
storeOfObjectTypes: StoreOfObjectTypes,
|
||||
getDefaultPageType: GetDefaultPageType,
|
||||
getTemplates: GetTemplates
|
||||
): ObjectSetViewModelFactory = ObjectSetViewModelFactory(
|
||||
openObjectSet = openObjectSet,
|
||||
closeBlock = closeBlock,
|
||||
|
@ -225,7 +227,9 @@ object ObjectSetModule {
|
|||
objectStore = objectStore,
|
||||
addObjectToCollection = addObjectToCollection,
|
||||
objectToCollection = convertObjectToCollection,
|
||||
storeOfObjectTypes = storeOfObjectTypes
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
getDefaultPageType = getDefaultPageType,
|
||||
getTemplates = getTemplates
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.widget.LinearLayout
|
|||
import android.widget.TextView
|
||||
import androidx.activity.addCallback
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.os.bundleOf
|
||||
|
@ -30,6 +31,7 @@ import androidx.core.view.updateLayoutParams
|
|||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
|
@ -46,7 +48,9 @@ import com.anytypeio.anytype.core_ui.reactive.clicks
|
|||
import com.anytypeio.anytype.core_ui.reactive.editorActionEvents
|
||||
import com.anytypeio.anytype.core_ui.reactive.touches
|
||||
import com.anytypeio.anytype.core_ui.tools.DefaultTextWatcher
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonPrimarySmallIcon
|
||||
import com.anytypeio.anytype.core_ui.widgets.FeaturedRelationGroupWidget
|
||||
import com.anytypeio.anytype.core_ui.widgets.ObjectTypeTemplatesWidget
|
||||
import com.anytypeio.anytype.core_ui.widgets.StatusBadgeWidget
|
||||
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
|
||||
import com.anytypeio.anytype.core_ui.widgets.toolbar.DataViewInfo
|
||||
|
@ -142,6 +146,9 @@ open class ObjectSetFragment :
|
|||
private val addNewButton: TextView
|
||||
get() = binding.dataViewHeader.addNewButton
|
||||
|
||||
private val addNewIconButton: ButtonPrimarySmallIcon
|
||||
get() = binding.dataViewHeader.addNewIconButton
|
||||
|
||||
private val customizeViewButton: ImageView
|
||||
get() = binding.dataViewHeader.customizeViewButton
|
||||
|
||||
|
@ -213,12 +220,14 @@ open class ObjectSetFragment :
|
|||
binding.root.setTransitionListener(transitionListener)
|
||||
|
||||
with(lifecycleScope) {
|
||||
subscribe(addNewButton.clicks().throttleFirst()) { vm.onCreateNewDataViewObject() }
|
||||
subscribe(addNewButton.clicks().throttleFirst()) { vm.proceedWithCreatingNewDataViewObject() }
|
||||
subscribe(addNewIconButton.buttonClicks()) { vm.proceedWithCreatingNewDataViewObject() }
|
||||
subscribe(addNewIconButton.iconClicks()) { vm.onNewButtonIconClicked() }
|
||||
subscribe(dataViewInfo.clicks().throttleFirst()) { type ->
|
||||
when (type) {
|
||||
DataViewInfo.TYPE.COLLECTION_NO_ITEMS -> vm.onCreateObjectInCollectionClicked()
|
||||
DataViewInfo.TYPE.SET_NO_QUERY -> vm.onSelectQueryButtonClicked()
|
||||
DataViewInfo.TYPE.SET_NO_ITEMS -> vm.onCreateNewDataViewObject()
|
||||
DataViewInfo.TYPE.SET_NO_ITEMS -> vm.proceedWithCreatingNewDataViewObject()
|
||||
DataViewInfo.TYPE.INIT -> {}
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +320,18 @@ open class ObjectSetFragment :
|
|||
toast("Error while setting the Set query. The query is empty")
|
||||
}
|
||||
}
|
||||
|
||||
binding.templatesWidget.apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
ObjectTypeTemplatesWidget(
|
||||
state = vm.templatesWidgetState.collectAsStateWithLifecycle().value,
|
||||
onDismiss = vm::onDismissTemplatesWidget,
|
||||
itemClick = vm::onTemplateItemClicked,
|
||||
scope = lifecycleScope
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupWindowInsetAnimation() {
|
||||
|
@ -432,7 +453,7 @@ open class ObjectSetFragment :
|
|||
header.visible()
|
||||
dataViewHeader.visible()
|
||||
viewerTitle.isEnabled = true
|
||||
addNewButton.isEnabled = true
|
||||
setupNewButtons(state.hasTemplates)
|
||||
customizeViewButton.isEnabled = true
|
||||
setCurrentViewerName(state.title)
|
||||
dataViewInfo.show(DataViewInfo.TYPE.COLLECTION_NO_ITEMS)
|
||||
|
@ -445,7 +466,7 @@ open class ObjectSetFragment :
|
|||
initView.gone()
|
||||
dataViewHeader.visible()
|
||||
viewerTitle.isEnabled = true
|
||||
addNewButton.isEnabled = true
|
||||
setupNewButtons(state.hasTemplates)
|
||||
customizeViewButton.isEnabled = true
|
||||
setCurrentViewerName(state.viewer?.title)
|
||||
dataViewInfo.hide()
|
||||
|
@ -471,12 +492,11 @@ open class ObjectSetFragment :
|
|||
header.visible()
|
||||
dataViewHeader.visible()
|
||||
viewerTitle.isEnabled = true
|
||||
addNewButton.isEnabled = true
|
||||
setupNewButtons(state.hasTemplates)
|
||||
customizeViewButton.isEnabled = true
|
||||
setCurrentViewerName(state.title)
|
||||
dataViewInfo.show(type = DataViewInfo.TYPE.SET_NO_ITEMS)
|
||||
setViewer(viewer = null)
|
||||
|
||||
}
|
||||
is DataViewViewState.Set.Default -> {
|
||||
topToolbarThreeDotsButton.visible()
|
||||
|
@ -485,7 +505,7 @@ open class ObjectSetFragment :
|
|||
header.visible()
|
||||
dataViewHeader.visible()
|
||||
viewerTitle.isEnabled = true
|
||||
addNewButton.isEnabled = true
|
||||
setupNewButtons(state.hasTemplates)
|
||||
customizeViewButton.isEnabled = true
|
||||
setCurrentViewerName(state.viewer?.title)
|
||||
setViewer(viewer = state.viewer)
|
||||
|
@ -521,6 +541,17 @@ open class ObjectSetFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupNewButtons(isTemplatesAllowed: Boolean) {
|
||||
if (isTemplatesAllowed) {
|
||||
addNewButton.gone()
|
||||
addNewIconButton.visible()
|
||||
} else {
|
||||
addNewButton.visible()
|
||||
addNewButton.isEnabled = true
|
||||
addNewIconButton.gone()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setViewer(viewer: Viewer?) {
|
||||
when (viewer) {
|
||||
is Viewer.GridView -> {
|
||||
|
@ -1063,10 +1094,16 @@ open class ObjectSetFragment :
|
|||
if (childFragmentManager.backStackEntryCount > 0) {
|
||||
childFragmentManager.popBackStack()
|
||||
} else {
|
||||
if (vm.isCustomizeViewPanelVisible.value) {
|
||||
vm.onHideViewerCustomizeSwiped()
|
||||
} else {
|
||||
vm.onSystemBackPressed()
|
||||
when {
|
||||
vm.isCustomizeViewPanelVisible.value -> {
|
||||
vm.onHideViewerCustomizeSwiped()
|
||||
}
|
||||
vm.templatesWidgetState.value.showWidget -> {
|
||||
vm.onDismissTemplatesWidget()
|
||||
}
|
||||
else -> {
|
||||
vm.onSystemBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,14 @@ package com.anytypeio.anytype.ui.templates
|
|||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateSelectViewModel
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
|
||||
class TemplateSelectAdapter(
|
||||
private var items: List<TemplateSelectViewModel.TemplateView>,
|
||||
private var items: List<TemplateView>,
|
||||
fragment: Fragment
|
||||
) : FragmentStateAdapter(fragment) {
|
||||
|
||||
fun update(newItems: List<TemplateSelectViewModel.TemplateView>) {
|
||||
fun update(newItems: List<TemplateView>) {
|
||||
items = newItems
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
@ -18,12 +18,12 @@ class TemplateSelectAdapter(
|
|||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (val templateView = items[position]) {
|
||||
is TemplateSelectViewModel.TemplateView.Blank -> TemplateBlankFragment.new(
|
||||
is TemplateView.Blank -> TemplateBlankFragment.new(
|
||||
typeId = templateView.typeId,
|
||||
typeName = templateView.typeName,
|
||||
layout = templateView.layout
|
||||
)
|
||||
is TemplateSelectViewModel.TemplateView.Template -> TemplateFragment.new(
|
||||
is TemplateView.Template -> TemplateFragment.new(
|
||||
templateView.id
|
||||
)
|
||||
}
|
||||
|
|
|
@ -148,4 +148,12 @@
|
|||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/templatesWidget"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:motion="http://schemas.android.com/apk/res-auto">
|
||||
xmlns:motion="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:app="http://schemas.android.com/tools">
|
||||
|
||||
<Transition
|
||||
motion:constraintSetEnd="@+id/end"
|
||||
|
@ -90,6 +91,12 @@
|
|||
motion:layout_constraintStart_toStartOf="parent"
|
||||
motion:layout_constraintTop_toTopOf="parent"
|
||||
motion:visibilityMode="ignore" />
|
||||
<Constraint
|
||||
android:id="@id/templatesWidget">
|
||||
<PropertySet
|
||||
app:applyMotionScene="false"
|
||||
app:visibilityMode="ignore"/>
|
||||
</Constraint>
|
||||
</ConstraintSet>
|
||||
|
||||
<ConstraintSet android:id="@+id/end">
|
||||
|
@ -171,6 +178,13 @@
|
|||
motion:layout_constraintStart_toStartOf="parent"
|
||||
motion:layout_constraintTop_toTopOf="parent"
|
||||
motion:visibilityMode="ignore" />
|
||||
|
||||
<Constraint
|
||||
android:id="@id/templatesWidget">
|
||||
<PropertySet
|
||||
app:applyMotionScene="false"
|
||||
app:visibilityMode="ignore"/>
|
||||
</Constraint>
|
||||
</ConstraintSet>
|
||||
|
||||
</MotionScene>
|
|
@ -25,6 +25,25 @@ object ObjectTypeIds {
|
|||
const val SPACE = "ot-space"
|
||||
|
||||
const val DEFAULT_OBJECT_TYPE_PREFIX = "ot-"
|
||||
|
||||
fun getTypesWithoutTemplates(): List<String> =
|
||||
listOf(BOOKMARK, NOTE).plus(getFileTypes()).plus(getSetTypes())
|
||||
.plus(getSystemTypes())
|
||||
|
||||
fun getFileTypes(): List<String> = listOf(FILE, IMAGE, AUDIO, VIDEO)
|
||||
|
||||
fun getSystemTypes(): List<String> = listOf(
|
||||
OBJECT_TYPE,
|
||||
TEMPLATE,
|
||||
RELATION,
|
||||
RELATION_OPTION,
|
||||
DASHBOARD,
|
||||
DATE,
|
||||
MarketplaceObjectTypeIds.OBJECT_TYPE,
|
||||
MarketplaceObjectTypeIds.RELATION
|
||||
)
|
||||
|
||||
fun getSetTypes(): List<String> = listOf(SET, COLLECTION)
|
||||
}
|
||||
|
||||
object MarketplaceObjectTypeIds {
|
||||
|
|
|
@ -2,6 +2,10 @@ package com.anytypeio.anytype.core_ui.views
|
|||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
|
@ -33,8 +37,10 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.reactive.clicks
|
||||
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
|
||||
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
|
||||
import com.anytypeio.anytype.core_utils.ext.throttleFirst
|
||||
|
||||
class ButtonPrimaryXSmall @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -563,4 +569,30 @@ fun MyWarningButton() {
|
|||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
class ButtonPrimarySmallIcon @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : LinearLayout(context, attrs) {
|
||||
|
||||
private lateinit var button: TextView
|
||||
private lateinit var icon: ImageView
|
||||
|
||||
init {
|
||||
setup(context)
|
||||
}
|
||||
|
||||
private fun setup(context: Context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.ds_button_icon, this, true)
|
||||
button = findViewById(R.id.button)
|
||||
icon = findViewById(R.id.icon)
|
||||
}
|
||||
|
||||
fun setButtonText(text: String) {
|
||||
button.text = text
|
||||
}
|
||||
|
||||
fun buttonClicks() = button.clicks().throttleFirst()
|
||||
fun iconClicks() = icon.clicks().throttleFirst()
|
||||
}
|
|
@ -267,6 +267,14 @@ val Caption2Regular = TextStyle(
|
|||
letterSpacing = (-0.006).em
|
||||
)
|
||||
|
||||
val Caption2Semibold = TextStyle(
|
||||
fontFamily = fontInterSemibold,
|
||||
fontWeight = FontWeight.W600,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 14.sp,
|
||||
letterSpacing = (-0.006).em
|
||||
)
|
||||
|
||||
//UX/Button/Medium
|
||||
val ButtonMedium = TextStyle(
|
||||
fontFamily = fontInterMedium,
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
package com.anytypeio.anytype.core_ui.widgets
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.FractionalThreshold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.rememberSwipeableState
|
||||
import androidx.compose.material.swipeable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
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.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
|
||||
import com.anytypeio.anytype.core_ui.views.Caption2Semibold
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
import com.anytypeio.anytype.presentation.widgets.TemplatesWidgetUiState
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun ObjectTypeTemplatesWidget(
|
||||
state: TemplatesWidgetUiState,
|
||||
onDismiss: () -> Unit,
|
||||
itemClick: (TemplateView) -> Unit,
|
||||
scope: CoroutineScope
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
) {
|
||||
|
||||
val currentState by rememberUpdatedState(state)
|
||||
val swipeableState = rememberSwipeableState(DragStates.VISIBLE)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = currentState.showWidget,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(tween(200)
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.4f))
|
||||
.noRippleClickable { onDismiss() }
|
||||
)
|
||||
}
|
||||
|
||||
if (swipeableState.isAnimationRunning) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentState.showWidget) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
scope.launch { swipeableState.snapTo(DragStates.VISIBLE) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sizePx = with(LocalDensity.current) { 312.dp.toPx() }
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = currentState.showWidget,
|
||||
enter = slideInVertically { it },
|
||||
exit = slideOutVertically(tween(200)) { it },
|
||||
modifier = Modifier
|
||||
.swipeable(
|
||||
state = swipeableState,
|
||||
orientation = Orientation.Vertical,
|
||||
anchors = mapOf(
|
||||
0f to DragStates.VISIBLE,
|
||||
sizePx to DragStates.DISMISSED
|
||||
),
|
||||
thresholds = { _, _ -> FractionalThreshold(0.3f) }
|
||||
)
|
||||
.offset { IntOffset(0, swipeableState.offset.value.roundToInt()) }
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(312.dp)
|
||||
.padding(start = 8.dp, end = 8.dp, bottom = 31.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.background_primary),
|
||||
shape = RoundedCornerShape(size = 16.dp)
|
||||
)
|
||||
,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(bottom = 24.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
) {
|
||||
// Box(
|
||||
// modifier = Modifier
|
||||
// .align(Alignment.CenterStart),
|
||||
// ) {
|
||||
// Text(
|
||||
// modifier = Modifier.padding(
|
||||
// start = 16.dp,
|
||||
// top = 12.dp,
|
||||
// bottom = 12.dp,
|
||||
// end = 16.dp
|
||||
// ),
|
||||
// text = stringResource(id = R.string.edit),
|
||||
// style = BodyCalloutRegular
|
||||
// )
|
||||
// }
|
||||
Box(modifier = Modifier.align(Alignment.Center)) {
|
||||
Text(
|
||||
text = stringResource(R.string.type_templates_widget_title),
|
||||
style = Title1,
|
||||
color = colorResource(R.color.text_primary)
|
||||
)
|
||||
}
|
||||
// Box(modifier = Modifier.align(Alignment.CenterEnd)) {
|
||||
// Image(
|
||||
// modifier = Modifier.padding(
|
||||
// start = 16.dp,
|
||||
// top = 12.dp,
|
||||
// bottom = 12.dp,
|
||||
// end = 16.dp
|
||||
// ),
|
||||
// painter = painterResource(id = R.drawable.ic_default_plus),
|
||||
// contentDescription = null
|
||||
// )
|
||||
// }
|
||||
}
|
||||
TemplatesList(currentState.items) {
|
||||
scope.launch {
|
||||
onDismiss()
|
||||
delay(200L)
|
||||
itemClick(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TemplatesList(
|
||||
items: List<TemplateView>,
|
||||
itemClick: (TemplateView) -> Unit
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.height(224.dp)
|
||||
.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(start = 16.dp, end = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
)
|
||||
{
|
||||
itemsIndexed(
|
||||
items = items,
|
||||
itemContent = { index, item ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = colorResource(id = R.color.shape_primary),
|
||||
shape = RoundedCornerShape(size = 16.dp)
|
||||
)
|
||||
.height(224.dp)
|
||||
.width(120.dp)
|
||||
.clickable {
|
||||
itemClick(item)
|
||||
}
|
||||
) {
|
||||
Column {
|
||||
TemplateItemContent(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TemplateItemContent(item: TemplateView) {
|
||||
when (item) {
|
||||
is TemplateView.Blank -> {
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
TemplateItemTitle(text = stringResource(id = R.string.blank))
|
||||
}
|
||||
is TemplateView.Template -> {
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
TemplateItemTitle(text = item.name)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
TemplateItemRectangles()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TemplateItemTitle(text: String) {
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
text = text,
|
||||
style = Caption2Semibold.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TemplateItemRectangles() {
|
||||
Column {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp)
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_secondary),
|
||||
shape = RoundedCornerShape(size = 1.dp)
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp)
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_secondary),
|
||||
shape = RoundedCornerShape(size = 1.dp)
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp)
|
||||
.padding(start = 16.dp, end = 40.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_secondary),
|
||||
shape = RoundedCornerShape(size = 1.dp)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class DragStates {
|
||||
VISIBLE,
|
||||
DISMISSED
|
||||
}
|
||||
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ComposablePreview() {
|
||||
val items = listOf(
|
||||
TemplateView.Blank(
|
||||
typeId = "page",
|
||||
typeName = "Page",
|
||||
layout = ObjectType.Layout.BASIC.code
|
||||
),
|
||||
TemplateView.Template(
|
||||
id = "1",
|
||||
name = "Template 1",
|
||||
typeId = "page",
|
||||
layout = ObjectType.Layout.BASIC,
|
||||
image = null,
|
||||
emoji = null
|
||||
),
|
||||
)
|
||||
val state = TemplatesWidgetUiState(items = items, showWidget = true)
|
||||
ObjectTypeTemplatesWidget(state = state, onDismiss = {}, itemClick = {}, scope = CoroutineScope(
|
||||
Dispatchers.Main
|
||||
)
|
||||
)
|
||||
}
|
12
core-ui/src/main/res/drawable/ic_arrow_down_18.xml
Normal file
12
core-ui/src/main/res/drawable/ic_arrow_down_18.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<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="M4,7L9,12L14,7"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/text_color_button_primary_selector"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
33
core-ui/src/main/res/layout/ds_button_icon.xml
Normal file
33
core-ui/src/main/res/layout/ds_button_icon.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dp"
|
||||
android:background="@drawable/button_primary_xsmall_selector"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button"
|
||||
style="@style/TextView.UXStyle.Captions.1.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="New"
|
||||
android:textColor="@color/text_color_button_primary_selector" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ds_button_icon_divider" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_arrow_down_18" />
|
||||
|
||||
</LinearLayout>
|
|
@ -25,29 +25,15 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="@dimen/dp_16"
|
||||
android:contentDescription="@string/content_description_customize_view_button"
|
||||
android:paddingStart="@dimen/dp_20"
|
||||
android:paddingEnd="@dimen/dp_8"
|
||||
android:src="@drawable/bg_viewer_settings_icon_bg"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/addNewButton"
|
||||
app:layout_constraintEnd_toStartOf="@+id/barrier"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:enabled="true" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.views.ButtonPrimaryXSmall
|
||||
android:id="@+id/addNewButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/dp_20"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:text="@string/button_new"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
|
@ -56,4 +42,38 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="left"
|
||||
app:barrierMargin="-8dp"
|
||||
app:constraint_referenced_ids="addNewButton,addNewIconButton" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.views.ButtonPrimaryXSmall
|
||||
android:id="@+id/addNewButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/dp_20"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="@string/button_new"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.views.ButtonPrimarySmallIcon
|
||||
android:id="@+id/addNewIconButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="@dimen/dp_20"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="@string/button_new"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--typography, buttons 05.04-->
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -95,5 +95,6 @@
|
|||
<color name="snackbar_background">#FFFFFF</color>
|
||||
|
||||
<color name="widget_divider">#24DAD7CA</color>
|
||||
<color name="ds_button_icon_divider">#1A000000</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -210,5 +210,6 @@
|
|||
<color name="snackbar_background">#000000</color>
|
||||
|
||||
<color name="widget_divider">#E3E3E3</color>
|
||||
<color name="ds_button_icon_divider">#1AFFFFFF</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -643,5 +643,7 @@
|
|||
<string name="menu_type_open_set">Open set of %1$s</string>
|
||||
<string name="menu_type_create_set">Create set of %1$s</string>
|
||||
<string name="toast_active_view_delete">Current view cannot be deleted</string>
|
||||
<string name="type_templates_widget_title">Select template</string>
|
||||
<string name="blank">Blank</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -33,12 +33,12 @@ class CreateDataViewObject(
|
|||
return when (params) {
|
||||
is Params.SetByType -> {
|
||||
val command = Command.CreateObject(
|
||||
template = resolveTemplateForNewObject(type = params.type),
|
||||
template = params.template,
|
||||
prefilled = resolveSetByTypePrefilledObjectData(
|
||||
filters = params.filters,
|
||||
type = params.type
|
||||
),
|
||||
internalFlags = listOf(InternalFlags.ShouldSelectTemplate)
|
||||
internalFlags = listOf()
|
||||
)
|
||||
val result = repo.createObject(command)
|
||||
Result(
|
||||
|
@ -49,16 +49,13 @@ class CreateDataViewObject(
|
|||
is Params.SetByRelation -> {
|
||||
val type = resolveDefaultObjectType()
|
||||
val command = Command.CreateObject(
|
||||
template = resolveTemplateForNewObject(type = type),
|
||||
template = params.template,
|
||||
prefilled = resolveSetByRelationPrefilledObjectData(
|
||||
filters = params.filters,
|
||||
relations = params.relations,
|
||||
type = type
|
||||
),
|
||||
internalFlags = listOf(
|
||||
InternalFlags.ShouldSelectType,
|
||||
InternalFlags.ShouldSelectTemplate
|
||||
)
|
||||
internalFlags = listOf()
|
||||
)
|
||||
val result = repo.createObject(command)
|
||||
Result(
|
||||
|
@ -66,19 +63,16 @@ class CreateDataViewObject(
|
|||
objectType = type
|
||||
)
|
||||
}
|
||||
Params.Collection -> {
|
||||
is Params.Collection -> {
|
||||
val type = resolveDefaultObjectType()
|
||||
val command = Command.CreateObject(
|
||||
template = resolveTemplateForNewObject(type = type),
|
||||
template = params.templateId,
|
||||
prefilled = resolveSetByRelationPrefilledObjectData(
|
||||
filters = emptyList(),
|
||||
relations = emptyList(),
|
||||
type = type
|
||||
),
|
||||
internalFlags = listOf(
|
||||
InternalFlags.ShouldSelectType,
|
||||
InternalFlags.ShouldSelectTemplate
|
||||
)
|
||||
internalFlags = listOf()
|
||||
)
|
||||
val result = repo.createObject(command)
|
||||
Result(
|
||||
|
@ -180,15 +174,19 @@ class CreateDataViewObject(
|
|||
sealed class Params {
|
||||
data class SetByType(
|
||||
val type: Id,
|
||||
val filters: List<DVFilter>
|
||||
val filters: List<DVFilter>,
|
||||
val template: Id?
|
||||
) : Params()
|
||||
|
||||
data class SetByRelation(
|
||||
val filters: List<DVFilter>,
|
||||
val relations: List<Id>
|
||||
val relations: List<Id>,
|
||||
val template: Id?
|
||||
) : Params()
|
||||
|
||||
object Collection : Params()
|
||||
data class Collection(
|
||||
val templateId: Id?
|
||||
) : Params()
|
||||
}
|
||||
|
||||
data class Result(
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.anytypeio.anytype.domain.templates
|
||||
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.DVFilter
|
||||
import com.anytypeio.anytype.core_models.DVFilterCondition
|
||||
import com.anytypeio.anytype.core_models.DVSort
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
|
@ -10,7 +12,6 @@ import com.anytypeio.anytype.core_models.Relations
|
|||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
|
@ -28,13 +29,13 @@ class GetTemplates(
|
|||
filters = listOf(
|
||||
DVFilter(
|
||||
relation = Relations.IS_ARCHIVED,
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = false
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = true
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.IS_DELETED,
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = false
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = true
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.TYPE,
|
||||
|
@ -47,11 +48,25 @@ class GetTemplates(
|
|||
value = params.type
|
||||
)
|
||||
),
|
||||
keys = listOf(Relations.ID, Relations.NAME),
|
||||
sorts = emptyList(),
|
||||
keys = listOf(
|
||||
Relations.ID,
|
||||
Relations.NAME,
|
||||
Relations.LAYOUT,
|
||||
Relations.ICON_EMOJI,
|
||||
Relations.ICON_IMAGE,
|
||||
Relations.ICON_OPTION,
|
||||
Relations.COVER_ID,
|
||||
Relations.COVER_TYPE
|
||||
),
|
||||
sorts = listOf(
|
||||
DVSort(
|
||||
relationKey = Relations.CREATED_DATE,
|
||||
type = Block.Content.DataView.Sort.Type.DESC
|
||||
)
|
||||
),
|
||||
fulltext = "",
|
||||
offset = 0,
|
||||
limit = 0
|
||||
limit = 100
|
||||
).map { obj ->
|
||||
ObjectWrapper.Basic(obj)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ kotlinVersion = '1.7.10'
|
|||
|
||||
androidxCoreVersion = "1.10.1"
|
||||
|
||||
androidxComposeVersion = '1.3.1'
|
||||
androidxComposeVersion = '1.4.3'
|
||||
composeKotlinCompilerVersion = '1.3.1'
|
||||
composeMaterial3Version = '1.1.1'
|
||||
composeMaterialVersion = '1.3.1'
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package com.anytypeio.anytype.presentation.objects
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.BOOKMARK
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.COLLECTION
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.SET
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.presentation.mapper.toObjectTypeView
|
||||
import com.anytypeio.anytype.presentation.objects.SupportedLayouts.editorLayouts
|
||||
|
||||
/**
|
||||
* The method allows you to get object type views for using in the editor and set
|
||||
|
@ -49,4 +51,16 @@ fun List<ObjectWrapper.Type>.getObjectTypeViewsForSBPage(
|
|||
return@forEach
|
||||
}
|
||||
return result.sortedWith(ObjectTypeViewComparator())
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This method is used to understand if objects of this type can use templates.
|
||||
*
|
||||
* @return `true` if templates are allowed for this type of object, `false` otherwise.
|
||||
*/
|
||||
fun ObjectWrapper.Type.isTemplatesAllowed(): Boolean {
|
||||
val showTemplates = !ObjectTypeIds.getTypesWithoutTemplates().contains(this.id)
|
||||
val allowedObject = editorLayouts.contains(recommendedLayout)
|
||||
return showTemplates && allowedObject
|
||||
}
|
|
@ -7,15 +7,15 @@ sealed class DataViewViewState {
|
|||
|
||||
sealed class Collection : DataViewViewState() {
|
||||
object NoView : Collection()
|
||||
data class NoItems(val title: String) : Collection()
|
||||
data class Default(val viewer: Viewer?) : Collection()
|
||||
data class NoItems(val title: String, val hasTemplates: Boolean = false) : Collection()
|
||||
data class Default(val viewer: Viewer?, val hasTemplates: Boolean = false) : Collection()
|
||||
}
|
||||
|
||||
sealed class Set : DataViewViewState() {
|
||||
object NoQuery : Set()
|
||||
object NoView : Set()
|
||||
data class NoItems(val title: String) : Set()
|
||||
data class Default(val viewer: Viewer?) : Set()
|
||||
data class NoItems(val title: String, val hasTemplates: Boolean) : Set()
|
||||
data class Default(val viewer: Viewer?, val hasTemplates: Boolean) : Set()
|
||||
}
|
||||
|
||||
object Init: DataViewViewState()
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVSor
|
|||
import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerFields
|
||||
import com.anytypeio.anytype.core_models.Event.Command.DataView.UpdateView.DVViewerRelationUpdate
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
|
@ -20,22 +21,26 @@ import com.anytypeio.anytype.core_utils.ext.addAfterIndexInLine
|
|||
import com.anytypeio.anytype.core_utils.ext.mapInPlace
|
||||
import com.anytypeio.anytype.core_utils.ext.moveAfterIndexInLine
|
||||
import com.anytypeio.anytype.core_utils.ext.moveOnTop
|
||||
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.getProperName
|
||||
import com.anytypeio.anytype.presentation.objects.isTemplatesAllowed
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectSetConfig.ID_KEY
|
||||
import com.anytypeio.anytype.presentation.relations.isSystemKey
|
||||
import com.anytypeio.anytype.presentation.relations.title
|
||||
import com.anytypeio.anytype.presentation.relations.type
|
||||
import com.anytypeio.anytype.presentation.relations.view
|
||||
import com.anytypeio.anytype.presentation.sets.model.ObjectView
|
||||
import com.anytypeio.anytype.presentation.sets.model.SimpleRelationView
|
||||
import com.anytypeio.anytype.presentation.sets.model.Viewer
|
||||
import com.anytypeio.anytype.presentation.sets.state.ObjectState
|
||||
import timber.log.Timber
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
|
||||
fun ObjectState.DataView.featuredRelations(
|
||||
ctx: Id,
|
||||
|
@ -370,6 +375,35 @@ fun ObjectState.DataView.filterOutDeletedAndMissingObjects(query: List<Id>): Lis
|
|||
return query.filter(::isValidObject)
|
||||
}
|
||||
|
||||
suspend fun ObjectState.DataView.Set.isTemplatesAllowed(
|
||||
setOfValue: List<Id>,
|
||||
storeOfObjectTypes: StoreOfObjectTypes,
|
||||
getDefaultPageType: GetDefaultPageType
|
||||
): Boolean {
|
||||
val objectDetails = details[setOfValue.first()]?.map.orEmpty()
|
||||
return when (objectDetails.type){
|
||||
ObjectTypeIds.OBJECT_TYPE -> {
|
||||
val objectWrapper = ObjectWrapper.Type(objectDetails)
|
||||
objectWrapper.isTemplatesAllowed()
|
||||
}
|
||||
ObjectTypeIds.RELATION -> {
|
||||
//We have set of relations, need to check default object type
|
||||
storeOfObjectTypes.isTemplatesAllowedForDefaultType(getDefaultPageType)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun StoreOfObjectTypes.isTemplatesAllowedForDefaultType(getDefaultPageType: GetDefaultPageType): Boolean {
|
||||
try {
|
||||
val defaultObjectType = getDefaultPageType.run(Unit).type ?: return false
|
||||
val defaultObjType = get(defaultObjectType) ?: return false
|
||||
return defaultObjType.isTemplatesAllowed()
|
||||
} catch (e: Exception){
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun ObjectState.DataView.isValidObject(objectId: Id): Boolean {
|
||||
val objectDetails = details[objectId] ?: return false
|
||||
val objectWrapper = ObjectWrapper.Basic(objectDetails.map)
|
||||
|
@ -394,4 +428,22 @@ fun Viewer.isEmpty(): Boolean =
|
|||
is Viewer.GridView -> this.rows.isEmpty()
|
||||
is Viewer.ListView -> this.items.isEmpty()
|
||||
is Viewer.Unsupported -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun ObjectWrapper.Basic.toTemplateView(typeId: Id): TemplateView.Template {
|
||||
return TemplateView.Template(
|
||||
id = id,
|
||||
name = name.orEmpty(),
|
||||
typeId = typeId,
|
||||
emoji = iconEmoji,
|
||||
image = iconImage,
|
||||
layout = layout ?: ObjectType.Layout.BASIC
|
||||
)
|
||||
}
|
||||
|
||||
fun ObjectWrapper.Basic.toTemplateViewBlank(typeId: Id): TemplateView.Blank {
|
||||
return TemplateView.Blank(
|
||||
typeId = typeId,
|
||||
layout = layout?.code ?: ObjectType.Layout.BASIC.code
|
||||
)
|
||||
}
|
|
@ -26,6 +26,7 @@ import com.anytypeio.anytype.domain.cover.SetDocCoverImage
|
|||
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
|
||||
import com.anytypeio.anytype.domain.error.Error
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
|
@ -40,6 +41,7 @@ import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
|
|||
import com.anytypeio.anytype.domain.sets.OpenObjectSet
|
||||
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
import com.anytypeio.anytype.domain.templates.GetTemplates
|
||||
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.common.Action
|
||||
|
@ -54,6 +56,7 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEve
|
|||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent
|
||||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.navigation.SupportNavigation
|
||||
import com.anytypeio.anytype.presentation.objects.isTemplatesAllowed
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectSetConfig.DEFAULT_LIMIT
|
||||
import com.anytypeio.anytype.presentation.relations.RelationListViewModel
|
||||
|
@ -63,7 +66,9 @@ import com.anytypeio.anytype.presentation.sets.model.Viewer
|
|||
import com.anytypeio.anytype.presentation.sets.state.ObjectState
|
||||
import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer
|
||||
import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.widgets.TemplatesWidgetUiState
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
|
@ -116,7 +121,9 @@ class ObjectSetViewModel(
|
|||
private val objectStore: ObjectStore,
|
||||
private val addObjectToCollection: AddObjectToCollection,
|
||||
private val objectToCollection: ConvertObjectToCollection,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val getDefaultPageType: GetDefaultPageType,
|
||||
private val getTemplates: GetTemplates
|
||||
) : ViewModel(), SupportNavigation<EventWrapper<AppNavigation.Command>> {
|
||||
|
||||
val status = MutableStateFlow(SyncStatus.UNKNOWN)
|
||||
|
@ -144,12 +151,15 @@ class ObjectSetViewModel(
|
|||
MutableStateFlow(DataViewViewState.Init)
|
||||
val currentViewer = _currentViewer
|
||||
|
||||
private val _templateViews = MutableStateFlow<List<TemplateView>>(emptyList())
|
||||
|
||||
private val _header = MutableStateFlow<SetOrCollectionHeaderState>(
|
||||
SetOrCollectionHeaderState.None
|
||||
)
|
||||
val header: StateFlow<SetOrCollectionHeaderState> = _header
|
||||
|
||||
val isCustomizeViewPanelVisible = MutableStateFlow(false)
|
||||
val templatesWidgetState = MutableStateFlow(TemplatesWidgetUiState.empty())
|
||||
|
||||
@Deprecated("could be deleted")
|
||||
val isLoading = MutableStateFlow(false)
|
||||
|
@ -173,6 +183,7 @@ class ObjectSetViewModel(
|
|||
urlBuilder = urlBuilder,
|
||||
coverImageHashProvider = coverImageHashProvider
|
||||
)
|
||||
gettingTemplatesForDataViewState(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,9 +416,10 @@ class ObjectSetViewModel(
|
|||
combine(
|
||||
database.index,
|
||||
stateReducer.state,
|
||||
session.currentViewerId
|
||||
) { dataViewState, objectState, currentViewId ->
|
||||
processViewState(dataViewState, objectState, currentViewId)
|
||||
session.currentViewerId,
|
||||
_templateViews
|
||||
) { dataViewState, objectState, currentViewId, templates ->
|
||||
processViewState(dataViewState, objectState, currentViewId, templates)
|
||||
}.distinctUntilChanged().collect { viewState ->
|
||||
Timber.d("subscribeToDataViewViewer, newViewerState:[$viewState]")
|
||||
_currentViewer.value = viewState
|
||||
|
@ -418,18 +430,21 @@ class ObjectSetViewModel(
|
|||
private suspend fun processViewState(
|
||||
dataViewState: DataViewState,
|
||||
objectState: ObjectState,
|
||||
currentViewId: String?
|
||||
currentViewId: String?,
|
||||
templates: List<TemplateView>
|
||||
): DataViewViewState {
|
||||
return when (objectState) {
|
||||
is ObjectState.DataView.Collection -> processCollectionState(
|
||||
dataViewState,
|
||||
objectState,
|
||||
currentViewId
|
||||
dataViewState = dataViewState,
|
||||
objectState = objectState,
|
||||
currentViewId = currentViewId,
|
||||
templates = templates
|
||||
)
|
||||
is ObjectState.DataView.Set -> processSetState(
|
||||
dataViewState,
|
||||
objectState,
|
||||
currentViewId
|
||||
dataViewState = dataViewState,
|
||||
objectState = objectState,
|
||||
currentViewId = currentViewId,
|
||||
templates = templates
|
||||
)
|
||||
ObjectState.Init -> DataViewViewState.Init
|
||||
ObjectState.ErrorLayout -> DataViewViewState.Error(msg = "Wrong layout, couldn't open object")
|
||||
|
@ -439,7 +454,8 @@ class ObjectSetViewModel(
|
|||
private suspend fun processCollectionState(
|
||||
dataViewState: DataViewState,
|
||||
objectState: ObjectState.DataView.Collection,
|
||||
currentViewId: String?
|
||||
currentViewId: String?,
|
||||
templates: List<TemplateView>
|
||||
): DataViewViewState {
|
||||
if (!objectState.isInitialized) return DataViewViewState.Init
|
||||
|
||||
|
@ -461,8 +477,26 @@ class ObjectSetViewModel(
|
|||
|
||||
when {
|
||||
viewer == null -> DataViewViewState.Collection.NoView
|
||||
viewer.isEmpty() -> DataViewViewState.Collection.NoItems(title = viewer.title)
|
||||
else -> DataViewViewState.Collection.Default(viewer = viewer)
|
||||
viewer.isEmpty() -> {
|
||||
val isTemplatesPresent = templates.isNotEmpty() &&
|
||||
storeOfObjectTypes.isTemplatesAllowedForDefaultType(
|
||||
getDefaultPageType = getDefaultPageType
|
||||
)
|
||||
DataViewViewState.Collection.NoItems(
|
||||
title = viewer.title,
|
||||
hasTemplates = isTemplatesPresent
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val isTemplatesPresent = templates.isNotEmpty() &&
|
||||
storeOfObjectTypes.isTemplatesAllowedForDefaultType(
|
||||
getDefaultPageType = getDefaultPageType
|
||||
)
|
||||
DataViewViewState.Collection.Default(
|
||||
viewer = viewer,
|
||||
hasTemplates = isTemplatesPresent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -471,7 +505,8 @@ class ObjectSetViewModel(
|
|||
private suspend fun processSetState(
|
||||
dataViewState: DataViewState,
|
||||
objectState: ObjectState.DataView.Set,
|
||||
currentViewId: String?
|
||||
currentViewId: String?,
|
||||
templates: List<TemplateView>
|
||||
): DataViewViewState {
|
||||
if (!objectState.isInitialized) return DataViewViewState.Init
|
||||
|
||||
|
@ -503,8 +538,30 @@ class ObjectSetViewModel(
|
|||
when {
|
||||
query.isEmpty() || setOfValue.isEmpty() -> DataViewViewState.Set.NoQuery
|
||||
render == null -> DataViewViewState.Set.NoView
|
||||
render.isEmpty() -> DataViewViewState.Set.NoItems(title = render.title)
|
||||
else -> DataViewViewState.Set.Default(viewer = render)
|
||||
render.isEmpty() -> {
|
||||
val isTemplatesAllowed = templates.isNotEmpty() &&
|
||||
objectState.isTemplatesAllowed(
|
||||
setOfValue,
|
||||
storeOfObjectTypes,
|
||||
getDefaultPageType
|
||||
)
|
||||
DataViewViewState.Set.NoItems(
|
||||
title = render.title,
|
||||
hasTemplates = isTemplatesAllowed
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val isTemplatesAllowed = templates.isNotEmpty() &&
|
||||
objectState.isTemplatesAllowed(
|
||||
setOfValue,
|
||||
storeOfObjectTypes,
|
||||
getDefaultPageType
|
||||
)
|
||||
DataViewViewState.Set.Default(
|
||||
viewer = render,
|
||||
hasTemplates = isTemplatesAllowed
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -810,16 +867,29 @@ class ObjectSetViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun onCreateNewDataViewObject() {
|
||||
Timber.d("onCreateNewRecord, ")
|
||||
fun proceedWithCreatingNewDataViewObject(templatesId: Id? = null) {
|
||||
Timber.d("proceedWithCreatingNewDataViewObject, templatesId:[$templatesId]")
|
||||
val state = stateReducer.state.value.dataViewState() ?: return
|
||||
when (state) {
|
||||
is ObjectState.DataView.Collection -> proceedWithAddingObjectToCollection()
|
||||
is ObjectState.DataView.Set -> proceedWithCreatingSetObject(state)
|
||||
is ObjectState.DataView.Collection -> proceedWithAddingObjectToCollection(templatesId)
|
||||
is ObjectState.DataView.Set -> proceedWithCreatingSetObject(state, templatesId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithCreatingSetObject(currentState: ObjectState.DataView.Set) {
|
||||
fun onNewButtonIconClicked() {
|
||||
Timber.d("onNewButtonIconClicked, ")
|
||||
templatesWidgetState.value = TemplatesWidgetUiState(
|
||||
items = _templateViews.value,
|
||||
showWidget = true
|
||||
)
|
||||
}
|
||||
|
||||
fun onDismissTemplatesWidget() {
|
||||
Timber.d("onDismissTemplatesWidget, ")
|
||||
templatesWidgetState.value = templatesWidgetState.value.copy(showWidget = false)
|
||||
}
|
||||
|
||||
private fun proceedWithCreatingSetObject(currentState: ObjectState.DataView.Set, templateId: Id?) {
|
||||
if (isRestrictionPresent(DataViewRestriction.CREATE_OBJECT)) {
|
||||
toast(NOT_ALLOWED)
|
||||
} else {
|
||||
|
@ -849,7 +919,8 @@ class ObjectSetViewModel(
|
|||
proceedWithCreatingDataViewObject(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = sourceId,
|
||||
filters = viewer.filters
|
||||
filters = viewer.filters,
|
||||
template = templateId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -858,7 +929,8 @@ class ObjectSetViewModel(
|
|||
proceedWithCreatingDataViewObject(
|
||||
CreateDataViewObject.Params.SetByRelation(
|
||||
filters = viewer.filters,
|
||||
relations = setObject.setOf
|
||||
relations = setObject.setOf,
|
||||
template = templateId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -879,8 +951,11 @@ class ObjectSetViewModel(
|
|||
proceedWithAddingObjectToCollection()
|
||||
}
|
||||
|
||||
private fun proceedWithAddingObjectToCollection() {
|
||||
proceedWithCreatingDataViewObject(CreateDataViewObject.Params.Collection) { result ->
|
||||
private fun proceedWithAddingObjectToCollection(templateId: Id? = null) {
|
||||
val createObjectParams = CreateDataViewObject.Params.Collection(
|
||||
templateId = templateId
|
||||
)
|
||||
proceedWithCreatingDataViewObject(createObjectParams) { result ->
|
||||
val params = AddObjectToCollection.Params(
|
||||
ctx = context,
|
||||
after = "",
|
||||
|
@ -917,7 +992,7 @@ class ObjectSetViewModel(
|
|||
|
||||
private suspend fun proceedWithNewDataViewObject(params: CreateDataViewObject.Params, newObject: Id) {
|
||||
when (params) {
|
||||
CreateDataViewObject.Params.Collection -> {
|
||||
is CreateDataViewObject.Params.Collection -> {
|
||||
proceedWithOpeningObject(newObject)
|
||||
}
|
||||
is CreateDataViewObject.Params.SetByRelation -> {
|
||||
|
@ -1391,6 +1466,84 @@ class ObjectSetViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
// region TEMPLATES
|
||||
private suspend fun gettingTemplatesForDataViewState(state: ObjectState.DataView) {
|
||||
when (state) {
|
||||
is ObjectState.DataView.Collection -> {
|
||||
proceedWithGettingTemplates(typeId = null)
|
||||
}
|
||||
is ObjectState.DataView.Set -> {
|
||||
val sourceId = proceedWithGettingSetSourceId(state)
|
||||
val sourceMap = state.details[sourceId] ?: return
|
||||
when (sourceMap.type.firstOrNull()) {
|
||||
ObjectTypeIds.RELATION -> proceedWithGettingTemplates(typeId = null)
|
||||
ObjectTypeIds.OBJECT_TYPE -> proceedWithGettingTemplates(typeId = sourceId)
|
||||
else -> { Timber.d("Ignoring type of source") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithGettingTemplates(typeId: Id?) {
|
||||
val objectType = resolveObjectType(typeId)
|
||||
if (objectType?.isTemplatesAllowed() == true) {
|
||||
viewModelScope.launch {
|
||||
getTemplates.async(GetTemplates.Params(objectType.id)).fold(
|
||||
onSuccess = { templates ->
|
||||
if (templates.isNotEmpty()) {
|
||||
_templateViews.value =
|
||||
listOf(templates.first().toTemplateViewBlank(objectType.id)) +
|
||||
templates.map { it.toTemplateView(typeId = objectType.id) }
|
||||
}
|
||||
},
|
||||
onFailure = { e ->
|
||||
Timber.e(e, "Error getting templates for type ${objectType.id}")
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Timber.d("Templates are not allowed for type:[${objectType?.id}]")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun resolveObjectType(type: Id?): ObjectWrapper.Type? {
|
||||
return if (type == null) {
|
||||
val defaultObjectType = getDefaultPageType.run(Unit).type ?: return null
|
||||
storeOfObjectTypes.get(defaultObjectType)
|
||||
} else {
|
||||
storeOfObjectTypes.get(type)
|
||||
}
|
||||
}
|
||||
|
||||
fun onTemplateItemClicked(item: TemplateView) {
|
||||
when(item) {
|
||||
is TemplateView.Blank -> {
|
||||
templatesWidgetState.value = TemplatesWidgetUiState.empty()
|
||||
proceedWithCreatingNewDataViewObject()
|
||||
}
|
||||
is TemplateView.Template -> {
|
||||
templatesWidgetState.value = TemplatesWidgetUiState.empty()
|
||||
proceedWithCreatingNewDataViewObject(templatesId = item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithGettingSetSourceId(currentState: ObjectState.DataView.Set): Id? {
|
||||
if (isRestrictionPresent(DataViewRestriction.CREATE_OBJECT) || !currentState.isInitialized) {
|
||||
toast(NOT_ALLOWED)
|
||||
return null
|
||||
}
|
||||
|
||||
val setObject = ObjectWrapper.Basic(currentState.details[context]?.map ?: emptyMap())
|
||||
val sourceId = setObject.setOf.singleOrNull()
|
||||
if (sourceId == null) {
|
||||
Timber.e("Unable to define a source for a new object.")
|
||||
toast("Unable to define a source for a new object.")
|
||||
}
|
||||
return sourceId
|
||||
}
|
||||
//endregion
|
||||
|
||||
companion object {
|
||||
const val NOT_ALLOWED = "Not allowed for this set"
|
||||
const val NOT_ALLOWED_CELL = "Not allowed for this cell"
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.anytypeio.anytype.domain.collections.AddObjectToCollection
|
|||
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
|
||||
import com.anytypeio.anytype.domain.objects.ObjectStore
|
||||
|
@ -22,6 +23,7 @@ import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
|
|||
import com.anytypeio.anytype.domain.sets.OpenObjectSet
|
||||
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
import com.anytypeio.anytype.domain.templates.GetTemplates
|
||||
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.common.Action
|
||||
|
@ -60,7 +62,9 @@ class ObjectSetViewModelFactory(
|
|||
private val objectStore: ObjectStore,
|
||||
private val addObjectToCollection: AddObjectToCollection,
|
||||
private val objectToCollection: ConvertObjectToCollection,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val getDefaultPageType: GetDefaultPageType,
|
||||
private val getTemplates: GetTemplates
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
|
@ -93,7 +97,9 @@ class ObjectSetViewModelFactory(
|
|||
objectStore = objectStore,
|
||||
addObjectToCollection = addObjectToCollection,
|
||||
objectToCollection = objectToCollection,
|
||||
storeOfObjectTypes = storeOfObjectTypes
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
getDefaultPageType = getDefaultPageType,
|
||||
getTemplates = getTemplates
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ sealed class ObjectState {
|
|||
override val details: Map<Id, Block.Fields> = emptyMap(),
|
||||
override val objectRestrictions: List<ObjectRestriction> = emptyList(),
|
||||
override val dataViewRestrictions: List<DataViewRestrictions> = emptyList(),
|
||||
override val objectRelationLinks: List<RelationLink> = emptyList()
|
||||
override val objectRelationLinks: List<RelationLink> = emptyList(),
|
||||
) : DataView() {
|
||||
|
||||
override val isInitialized get() = blocks.any { it.content is DV }
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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_utils.common.EventWrapper
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
|
@ -71,7 +72,16 @@ class TemplateSelectViewModel(
|
|||
layout = objType.recommendedLayout?.code ?: 0
|
||||
)
|
||||
)
|
||||
addAll(templates.map { TemplateView.Template(it.id) })
|
||||
addAll(templates.map {
|
||||
TemplateView.Template(
|
||||
id = it.id,
|
||||
name = it.name.orEmpty(),
|
||||
layout = it.layout ?: ObjectType.Layout.BASIC,
|
||||
emoji = it.iconEmoji.orEmpty(),
|
||||
image = it.iconImage.orEmpty(),
|
||||
typeId = objType.id,
|
||||
)
|
||||
})
|
||||
}
|
||||
_viewState.emit(
|
||||
ViewState.Success(
|
||||
|
@ -142,11 +152,4 @@ class TemplateSelectViewModel(
|
|||
object ErrorGettingType : ViewState()
|
||||
}
|
||||
|
||||
sealed class TemplateView {
|
||||
data class Blank(
|
||||
val typeId: Id, val typeName: String, val layout: Int
|
||||
) : TemplateView()
|
||||
|
||||
data class Template(val id: Id) : TemplateView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.anytypeio.anytype.presentation.templates
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
|
||||
sealed class TemplateView {
|
||||
|
||||
data class Blank(
|
||||
val typeId: Id, val typeName: String = "", val layout: Int
|
||||
) : TemplateView()
|
||||
|
||||
data class Template(
|
||||
val id: Id,
|
||||
val name: String,
|
||||
val typeId: Id,
|
||||
val layout: ObjectType.Layout,
|
||||
val emoji: String?,
|
||||
val image: String?
|
||||
) : TemplateView()
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.anytypeio.anytype.presentation.widgets
|
||||
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
|
||||
data class TemplatesWidgetUiState(
|
||||
val items: List<TemplateView>,
|
||||
val showWidget: Boolean
|
||||
) {
|
||||
companion object {
|
||||
fun empty() = TemplatesWidgetUiState(
|
||||
items = emptyList(),
|
||||
showWidget = false
|
||||
)
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() {
|
|||
MockitoAnnotations.openMocks(this)
|
||||
viewModel = givenViewModel()
|
||||
objectCollection = MockCollection(context = root)
|
||||
stubGetDefaultPageType()
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -62,7 +63,6 @@ class CollectionAddRelationTest : ObjectSetViewModelTestSetup() {
|
|||
@Test
|
||||
fun `should add new relation to data view`() = runTest {
|
||||
// SETUP
|
||||
|
||||
stubWorkspaceManager(objectCollection.workspaceId)
|
||||
stubStoreOfRelations(objectCollection)
|
||||
stubSubscriptionResults(
|
||||
|
|
|
@ -30,6 +30,7 @@ class CollectionDataViewUpdateTest : ObjectSetViewModelTestSetup() {
|
|||
MockitoAnnotations.openMocks(this)
|
||||
viewModel = givenViewModel()
|
||||
objectCollection = MockCollection(context = root)
|
||||
stubGetDefaultPageType()
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
package com.anytypeio.anytype.presentation.collections
|
||||
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.domain.templates.GetTemplates
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoInteractions
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class CollectionTemplatesDelegateTest: ObjectSetViewModelTestSetup() {
|
||||
|
||||
private lateinit var viewModel: ObjectSetViewModel
|
||||
private lateinit var mockCollection: MockCollection
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
viewModel = givenViewModel()
|
||||
}
|
||||
|
||||
@After
|
||||
fun after() {
|
||||
rule.advanceTime()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should start get templates when collection with default type allowed templates`() = runTest {
|
||||
|
||||
val defaultType = ObjectTypeIds.PAGE
|
||||
val defaultTypeName = "Page"
|
||||
val defaultTypeMap = mapOf(
|
||||
Relations.ID to defaultType,
|
||||
Relations.TYPE to ObjectTypeIds.OBJECT_TYPE,
|
||||
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
|
||||
Relations.NAME to defaultTypeName
|
||||
)
|
||||
mockCollection = MockCollection(context = root)
|
||||
|
||||
// SETUP
|
||||
stubWorkspaceManager(mockCollection.workspaceId)
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubStoreOfObjectTypes(defaultTypeMap)
|
||||
stubGetDefaultPageType(type = defaultType, name = defaultTypeName)
|
||||
stubGetTemplates(type = defaultType)
|
||||
|
||||
val details = Block.Details(
|
||||
details = mapOf(
|
||||
root to Block.Fields(
|
||||
mapOf(
|
||||
Relations.ID to root,
|
||||
Relations.LAYOUT to ObjectType.Layout.COLLECTION.code.toDouble(),
|
||||
Relations.TYPE to ObjectTypeIds.COLLECTION
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
stubOpenObject(
|
||||
doc = listOf(mockCollection.header, mockCollection.title, mockCollection.dataView),
|
||||
details = details
|
||||
)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockCollection.subscriptionId,
|
||||
collection = root,
|
||||
workspace = mockCollection.workspaceId,
|
||||
storeOfRelations = storeOfRelations,
|
||||
keys = mockCollection.dvKeys,
|
||||
objects = listOf(mockCollection.obj1, mockCollection.obj2),
|
||||
dvSorts = mockCollection.sorts
|
||||
)
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
verify(getTemplates, times(1)).async(
|
||||
GetTemplates.Params(type = defaultType)
|
||||
)
|
||||
|
||||
verifyNoInteractions(createDataViewObject)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not start get templates when collection with default type not allowed templates`() = runTest {
|
||||
|
||||
val defaultType = ObjectTypeIds.NOTE
|
||||
val defaultTypeName = "Note"
|
||||
val defaultTypeMap = mapOf(
|
||||
Relations.ID to defaultType,
|
||||
Relations.TYPE to ObjectTypeIds.OBJECT_TYPE,
|
||||
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.NOTE.code.toDouble(),
|
||||
Relations.NAME to defaultTypeName
|
||||
)
|
||||
mockCollection = MockCollection(context = root)
|
||||
|
||||
// SETUP
|
||||
stubWorkspaceManager(mockCollection.workspaceId)
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubStoreOfObjectTypes(defaultTypeMap)
|
||||
stubGetDefaultPageType(type = defaultType, name = defaultTypeName)
|
||||
|
||||
val details = Block.Details(
|
||||
details = mapOf(
|
||||
root to Block.Fields(
|
||||
mapOf(
|
||||
Relations.ID to root,
|
||||
Relations.LAYOUT to ObjectType.Layout.COLLECTION.code.toDouble(),
|
||||
Relations.TYPE to ObjectTypeIds.COLLECTION
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
stubOpenObject(
|
||||
doc = listOf(mockCollection.header, mockCollection.title, mockCollection.dataView),
|
||||
details = details
|
||||
)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockCollection.subscriptionId,
|
||||
collection = root,
|
||||
workspace = mockCollection.workspaceId,
|
||||
storeOfRelations = storeOfRelations,
|
||||
keys = mockCollection.dvKeys,
|
||||
objects = listOf(mockCollection.obj1, mockCollection.obj2),
|
||||
dvSorts = mockCollection.sorts
|
||||
)
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.onNewButtonIconClicked()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
verifyNoInteractions(getTemplates)
|
||||
verifyNoInteractions(createDataViewObject)
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubsc
|
|||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import net.bytebuddy.utility.RandomString
|
||||
|
||||
class MockSet(context: String, setOfValue: String = "setOf-${RandomString.make()}") {
|
||||
class MockSet(context: String, val setOfValue: String = "setOf-${RandomString.make()}") {
|
||||
|
||||
val root = context
|
||||
val title =
|
||||
|
@ -34,7 +34,7 @@ class MockSet(context: String, setOfValue: String = "setOf-${RandomString.make()
|
|||
)
|
||||
val workspaceId = "workspace-${RandomString.make()}"
|
||||
val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(context)
|
||||
val setOf = setOfValue
|
||||
val setOf get() = setOfValue
|
||||
val setOfNote = ObjectTypeIds.NOTE
|
||||
|
||||
// RELATION OBJECTS
|
||||
|
@ -180,7 +180,8 @@ class MockSet(context: String, setOfValue: String = "setOf-${RandomString.make()
|
|||
setOf to Block.Fields(
|
||||
map = mapOf(
|
||||
Relations.ID to setOf,
|
||||
Relations.TYPE to ObjectTypeIds.OBJECT_TYPE
|
||||
Relations.TYPE to ObjectTypeIds.OBJECT_TYPE,
|
||||
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -31,6 +31,7 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
fun setup() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
viewModel = givenViewModel()
|
||||
stubGetDefaultPageType()
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -69,7 +70,8 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
doReturn(Resultat.success(result)).`when`(createDataViewObject).async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = ObjectTypeIds.NOTE,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
doReturn(Resultat.success(Unit)).`when`(closeBlock).async(mockObjectSet.root)
|
||||
|
@ -79,7 +81,7 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
|
@ -87,7 +89,8 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = ObjectTypeIds.NOTE,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -125,7 +128,8 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
doReturn(Resultat.success(result)).`when`(createDataViewObject).async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = ObjectTypeIds.PAGE,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
doReturn(Resultat.success(Unit)).`when`(closeBlock).async(mockObjectSet.root)
|
||||
|
@ -136,7 +140,7 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
assertIs<ObjectSetCommand.Modal.SetNameForCreatedObject>(commandFlow.awaitItem())
|
||||
|
||||
|
@ -146,7 +150,8 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = ObjectTypeIds.PAGE,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -184,7 +189,8 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
doReturn(Resultat.success(result)).`when`(createDataViewObject).async(
|
||||
CreateDataViewObject.Params.SetByRelation(
|
||||
relations = listOf(mockObjectSet.relationObject3.id),
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
doReturn(Resultat.success(Unit)).`when`(closeBlock).async(mockObjectSet.root)
|
||||
|
@ -194,7 +200,7 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
|
@ -202,7 +208,8 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
async(
|
||||
CreateDataViewObject.Params.SetByRelation(
|
||||
relations = listOf(mockObjectSet.relationObject3.id),
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -244,7 +251,9 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
objectType = ObjectTypeIds.NOTE
|
||||
)
|
||||
doReturn(Resultat.success(result)).`when`(createDataViewObject).async(
|
||||
CreateDataViewObject.Params.Collection
|
||||
CreateDataViewObject.Params.Collection(
|
||||
templateId = null
|
||||
)
|
||||
)
|
||||
doReturn(Resultat.success(Unit)).`when`(closeBlock).async(objectCollection.root)
|
||||
|
||||
|
@ -253,12 +262,12 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
verifyBlocking(createDataViewObject, times(1)) {
|
||||
async(CreateDataViewObject.Params.Collection)
|
||||
async(CreateDataViewObject.Params.Collection(null))
|
||||
}
|
||||
|
||||
verifyBlocking(closeBlock, times(1)) { async(objectCollection.root)}
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
package com.anytypeio.anytype.presentation.collections
|
||||
|
||||
import app.cash.turbine.testIn
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.StubObject
|
||||
import com.anytypeio.anytype.presentation.sets.DataViewViewState
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.SetOrCollectionHeaderState
|
||||
import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup
|
||||
import com.anytypeio.anytype.presentation.sets.model.Viewer
|
||||
import com.anytypeio.anytype.presentation.sets.state.ObjectState
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertTrue
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
@ -29,6 +36,7 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() {
|
|||
MockitoAnnotations.openMocks(this)
|
||||
viewModel = givenViewModel()
|
||||
mockObjectCollection = MockCollection(context = root)
|
||||
stubGetDefaultPageType()
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -487,6 +495,7 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() {
|
|||
),
|
||||
dvSorts = listOf(mockObjectCollection.sortGallery)
|
||||
)
|
||||
stubGetTemplates()
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
@ -510,4 +519,167 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() {
|
|||
assertEquals(mockObjectCollection.obj5.id, rows[4].objectId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be collection with templates present when default type is custom with proper recommended layout`() = runTest {
|
||||
// SETUP
|
||||
|
||||
val defaultObjectType = MockDataFactory.randomString()
|
||||
val defaultObjectTypeName = "CustomName"
|
||||
|
||||
stubWorkspaceManager(mockObjectCollection.workspaceId)
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubGetDefaultPageType(defaultObjectType, defaultObjectTypeName)
|
||||
stubGetTemplates(
|
||||
type = defaultObjectType,
|
||||
templates = listOf(StubObject(objectType = defaultObjectType))
|
||||
)
|
||||
|
||||
stubOpenObject(
|
||||
doc = listOf(
|
||||
mockObjectCollection.header,
|
||||
mockObjectCollection.title,
|
||||
mockObjectCollection.dataView
|
||||
),
|
||||
details = mockObjectCollection.details
|
||||
)
|
||||
stubStoreOfObjectTypes(
|
||||
mapOf(
|
||||
Relations.ID to defaultObjectType,
|
||||
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
|
||||
Relations.NAME to defaultObjectTypeName
|
||||
)
|
||||
)
|
||||
stubStoreOfRelations(mockObjectCollection)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockObjectCollection.subscriptionId,
|
||||
workspace = mockObjectCollection.workspaceId,
|
||||
collection = root,
|
||||
storeOfRelations = storeOfRelations,
|
||||
keys = mockObjectCollection.dvKeys,
|
||||
dvSorts = mockObjectCollection.sorts
|
||||
)
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
||||
val viewerFlow = viewModel.currentViewer.testIn(backgroundScope)
|
||||
val stateFlow = stateReducer.state.testIn(backgroundScope)
|
||||
|
||||
// ASSERT STATES
|
||||
assertIs<ObjectState.Init>(stateFlow.awaitItem())
|
||||
assertIs<DataViewViewState.Init>(viewerFlow.awaitItem())
|
||||
assertIs<ObjectState.DataView.Collection>(stateFlow.awaitItem())
|
||||
|
||||
val item = viewerFlow.awaitItem()
|
||||
assertIs<DataViewViewState.Collection.NoItems>(item)
|
||||
assertTrue(item.hasTemplates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be collection without templates allowed when default type is custom with not proper recommended layout`() = runTest {
|
||||
// SETUP
|
||||
|
||||
val defaultObjectType = MockDataFactory.randomString()
|
||||
val defaultObjectTypeName = "CustomName"
|
||||
|
||||
stubWorkspaceManager(mockObjectCollection.workspaceId)
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubGetDefaultPageType(defaultObjectType, defaultObjectTypeName)
|
||||
|
||||
stubOpenObject(
|
||||
doc = listOf(
|
||||
mockObjectCollection.header,
|
||||
mockObjectCollection.title,
|
||||
mockObjectCollection.dataView
|
||||
),
|
||||
details = mockObjectCollection.details
|
||||
)
|
||||
stubStoreOfObjectTypes(
|
||||
mapOf(
|
||||
Relations.ID to defaultObjectType,
|
||||
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.SET.code.toDouble(),
|
||||
Relations.NAME to defaultObjectTypeName
|
||||
)
|
||||
)
|
||||
stubStoreOfRelations(mockObjectCollection)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockObjectCollection.subscriptionId,
|
||||
workspace = mockObjectCollection.workspaceId,
|
||||
collection = root,
|
||||
storeOfRelations = storeOfRelations,
|
||||
keys = mockObjectCollection.dvKeys,
|
||||
dvSorts = mockObjectCollection.sorts
|
||||
)
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
||||
val viewerFlow = viewModel.currentViewer.testIn(backgroundScope)
|
||||
val stateFlow = stateReducer.state.testIn(backgroundScope)
|
||||
|
||||
// ASSERT STATES
|
||||
assertIs<ObjectState.Init>(stateFlow.awaitItem())
|
||||
assertIs<DataViewViewState.Init>(viewerFlow.awaitItem())
|
||||
assertIs<ObjectState.DataView.Collection>(stateFlow.awaitItem())
|
||||
|
||||
val item = viewerFlow.awaitItem()
|
||||
assertIs<DataViewViewState.Collection.NoItems>(item)
|
||||
assertFalse(item.hasTemplates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be collection without templates allowed when default type is NOTE`() = runTest {
|
||||
// SETUP
|
||||
|
||||
val defaultObjectType = ObjectTypeIds.NOTE
|
||||
val defaultObjectTypeName = "Note"
|
||||
|
||||
stubWorkspaceManager(mockObjectCollection.workspaceId)
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubGetDefaultPageType(defaultObjectType, defaultObjectTypeName)
|
||||
|
||||
stubOpenObject(
|
||||
doc = listOf(
|
||||
mockObjectCollection.header,
|
||||
mockObjectCollection.title,
|
||||
mockObjectCollection.dataView
|
||||
),
|
||||
details = mockObjectCollection.details
|
||||
)
|
||||
stubStoreOfObjectTypes(
|
||||
mapOf(
|
||||
Relations.ID to defaultObjectType,
|
||||
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.NOTE.code.toDouble(),
|
||||
Relations.NAME to defaultObjectTypeName
|
||||
)
|
||||
)
|
||||
stubStoreOfRelations(mockObjectCollection)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockObjectCollection.subscriptionId,
|
||||
workspace = mockObjectCollection.workspaceId,
|
||||
collection = root,
|
||||
storeOfRelations = storeOfRelations,
|
||||
keys = mockObjectCollection.dvKeys,
|
||||
dvSorts = mockObjectCollection.sorts
|
||||
)
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
||||
val viewerFlow = viewModel.currentViewer.testIn(backgroundScope)
|
||||
val stateFlow = stateReducer.state.testIn(backgroundScope)
|
||||
|
||||
// ASSERT STATES
|
||||
assertIs<ObjectState.Init>(stateFlow.awaitItem())
|
||||
assertIs<DataViewViewState.Init>(viewerFlow.awaitItem())
|
||||
assertIs<ObjectState.DataView.Collection>(stateFlow.awaitItem())
|
||||
|
||||
val item = viewerFlow.awaitItem()
|
||||
assertIs<DataViewViewState.Collection.NoItems>(item)
|
||||
assertFalse(item.hasTemplates)
|
||||
}
|
||||
}
|
|
@ -7,14 +7,18 @@ import com.anytypeio.anytype.core_models.ObjectType
|
|||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.StubObject
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectSetConfig
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import com.anytypeio.anytype.presentation.sets.DataViewViewState
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup
|
||||
import com.anytypeio.anytype.presentation.sets.state.ObjectState
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertTrue
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
@ -408,4 +412,92 @@ class ObjectStateSetViewTest : ObjectSetViewModelTestSetup() {
|
|||
stateFlow.ensureAllEventsConsumed()
|
||||
viewerFlow.ensureAllEventsConsumed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `displaying set with templates present when opening object set of pages with templates`() = runTest {
|
||||
// SETUP
|
||||
|
||||
mockObjectSet = MockSet(context = root, setOfValue = ObjectTypeIds.PAGE)
|
||||
val pageTypeMap = mapOf(
|
||||
Relations.ID to ObjectTypeIds.PAGE,
|
||||
Relations.TYPE to ObjectTypeIds.OBJECT_TYPE,
|
||||
Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
|
||||
Relations.NAME to MockDataFactory.randomString()
|
||||
)
|
||||
stubWorkspaceManager(mockObjectSet.workspaceId)
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubOpenObject(
|
||||
doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
|
||||
details = mockObjectSet.details
|
||||
)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockObjectSet.subscriptionId,
|
||||
workspace = mockObjectSet.workspaceId,
|
||||
storeOfRelations = storeOfRelations,
|
||||
keys = mockObjectSet.dvKeys,
|
||||
sources = listOf(ObjectTypeIds.PAGE),
|
||||
dvFilters = mockObjectSet.filters
|
||||
)
|
||||
stubStoreOfObjectTypes(pageTypeMap)
|
||||
stubGetTemplates(
|
||||
type = ObjectTypeIds.PAGE,
|
||||
templates = listOf(StubObject(objectType = ObjectTypeIds.PAGE)
|
||||
)
|
||||
)
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
||||
// ASSERT STATES
|
||||
val viewerFlow = viewModel.currentViewer.testIn(backgroundScope)
|
||||
val stateFlow = stateReducer.state.testIn(backgroundScope)
|
||||
|
||||
// ASSERT STATES
|
||||
assertIs<ObjectState.Init>(stateFlow.awaitItem())
|
||||
assertIs<ObjectState.DataView.Set>(stateFlow.awaitItem())
|
||||
assertIs<DataViewViewState.Init>(viewerFlow.awaitItem())
|
||||
|
||||
val item = viewerFlow.awaitItem()
|
||||
assertIs<DataViewViewState.Set.NoItems>(item)
|
||||
assertTrue(item.hasTemplates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `displaying set without templates allowed when opening object set of notes`() = runTest {
|
||||
// SETUP
|
||||
|
||||
mockObjectSet = MockSet(context = root, setOfValue = ObjectTypeIds.NOTE)
|
||||
stubWorkspaceManager(mockObjectSet.workspaceId)
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubOpenObject(
|
||||
doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
|
||||
details = mockObjectSet.details
|
||||
)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockObjectSet.subscriptionId,
|
||||
workspace = mockObjectSet.workspaceId,
|
||||
storeOfRelations = storeOfRelations,
|
||||
keys = mockObjectSet.dvKeys,
|
||||
sources = listOf(ObjectTypeIds.NOTE),
|
||||
dvFilters = mockObjectSet.filters
|
||||
)
|
||||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
|
||||
// ASSERT STATES
|
||||
val viewerFlow = viewModel.currentViewer.testIn(backgroundScope)
|
||||
val stateFlow = stateReducer.state.testIn(backgroundScope)
|
||||
|
||||
// ASSERT STATES
|
||||
assertIs<ObjectState.Init>(stateFlow.awaitItem())
|
||||
assertIs<ObjectState.DataView.Set>(stateFlow.awaitItem())
|
||||
assertIs<DataViewViewState.Init>(viewerFlow.awaitItem())
|
||||
|
||||
val item = viewerFlow.awaitItem()
|
||||
assertIs<DataViewViewState.Set.NoItems>(item)
|
||||
assertFalse(item.hasTemplates)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.anytypeio.anytype.presentation.objects
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.StubObjectType
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class ObjectTypeExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `isTemplateAllowed returns true when type is not in getNoTemplates and recommendedLayout is in editorLayouts`() {
|
||||
val objectType = StubObjectType(
|
||||
id = ObjectTypeIds.PAGE,
|
||||
recommendedLayout = ObjectType.Layout.BASIC.code.toDouble()
|
||||
)
|
||||
val result = objectType.isTemplatesAllowed()
|
||||
assertTrue(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isTemplateAllowed returns false when type is not in getNoTemplates and recommendedLayout is not in editorLayouts`() {
|
||||
val objectType = StubObjectType(
|
||||
id = ObjectTypeIds.PAGE,
|
||||
recommendedLayout = ObjectType.Layout.DASHBOARD.code.toDouble()
|
||||
)
|
||||
val result = objectType.isTemplatesAllowed()
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isTemplateAllowed returns false when type is BOOKMARK`() {
|
||||
val objectType = StubObjectType(
|
||||
id = ObjectTypeIds.BOOKMARK,
|
||||
recommendedLayout = ObjectType.Layout.BASIC.code.toDouble()
|
||||
)
|
||||
val result = objectType.isTemplatesAllowed()
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isTemplateAllowed returns false when type is FILE`() {
|
||||
val objectType = StubObjectType(
|
||||
id = ObjectTypeIds.FILE,
|
||||
recommendedLayout = ObjectType.Layout.BASIC.code.toDouble()
|
||||
)
|
||||
val result = objectType.isTemplatesAllowed()
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isTemplateAllowed returns false when type is NOTE`() {
|
||||
val objectType = StubObjectType(
|
||||
id = ObjectTypeIds.NOTE,
|
||||
recommendedLayout = ObjectType.Layout.NOTE.code.toDouble()
|
||||
)
|
||||
val result = objectType.isTemplatesAllowed()
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isTemplateAllowed returns false when type is SET`() {
|
||||
val objectType = StubObjectType(
|
||||
id = ObjectTypeIds.SET,
|
||||
recommendedLayout = ObjectType.Layout.SET.code.toDouble()
|
||||
)
|
||||
val result = objectType.isTemplatesAllowed()
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isTemplateAllowed returns false when type is COLLECTION`() {
|
||||
val objectType = StubObjectType(
|
||||
id = ObjectTypeIds.COLLECTION,
|
||||
recommendedLayout = ObjectType.Layout.COLLECTION.code.toDouble()
|
||||
)
|
||||
val result = objectType.isTemplatesAllowed()
|
||||
assertFalse(result)
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import com.anytypeio.anytype.core_models.StubFilter
|
|||
import com.anytypeio.anytype.core_models.StubRelationLink
|
||||
import com.anytypeio.anytype.core_models.StubSort
|
||||
import com.anytypeio.anytype.core_models.StubTitle
|
||||
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
|
||||
import com.anytypeio.anytype.presentation.sets.state.DefaultObjectStateReducer
|
||||
import com.anytypeio.anytype.presentation.sets.state.ObjectState
|
||||
import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer
|
||||
|
|
|
@ -32,6 +32,7 @@ class ObjectSetConvertToCollectionTest : ObjectSetViewModelTestSetup() {
|
|||
MockitoAnnotations.openMocks(this)
|
||||
viewModel = givenViewModel()
|
||||
mockObjectSet = MockSet(context = root)
|
||||
stubGetDefaultPageType()
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
|
@ -57,7 +57,8 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
doReturn(Unit).`when`(createDataViewObject).async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = mockObjectSet.setOf,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -72,14 +73,15 @@ class ObjectSetDataViewObjectCreateTest : ObjectSetViewModelTestSetup() {
|
|||
val second = awaitItem()
|
||||
assertIs<DataViewViewState.Set.Default>(second)
|
||||
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
advanceUntilIdle()
|
||||
verifyBlocking(createDataViewObject, times(1)) {
|
||||
async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = mockObjectSet.setOf,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class ObjectSetInitializationTest : ObjectSetViewModelTestSetup() {
|
|||
|
||||
// TESTING
|
||||
viewModel.onStart(ctx = root)
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
// ASSERT
|
||||
verifyNoInteractions(createObject)
|
||||
|
|
|
@ -118,7 +118,7 @@ class ObjectSetRestrictionsTest : ObjectSetViewModelTestSetup() {
|
|||
|
||||
// ASSERT ERROR TOAST
|
||||
viewModel.toasts.test {
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
assertEquals(ObjectSetViewModel.NOT_ALLOWED, awaitItem())
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.anytypeio.anytype.core_models.restrictions.DataViewRestrictions
|
|||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.interactor.UpdateText
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
|
||||
|
@ -23,6 +24,7 @@ import com.anytypeio.anytype.domain.config.Gateway
|
|||
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
|
@ -54,6 +56,7 @@ import com.anytypeio.anytype.presentation.sets.ObjectSetPaginator
|
|||
import com.anytypeio.anytype.presentation.sets.ObjectSetSession
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.state.DefaultObjectStateReducer
|
||||
import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer
|
||||
import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription
|
||||
import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription
|
||||
import com.anytypeio.anytype.presentation.sets.updateFormatForSubscription
|
||||
|
@ -147,6 +150,9 @@ open class ObjectSetViewModelTestSetup {
|
|||
@Mock
|
||||
lateinit var storeOfObjectTypes: StoreOfObjectTypes
|
||||
|
||||
@Mock
|
||||
lateinit var getDefaultPageType: GetDefaultPageType
|
||||
|
||||
var stateReducer = DefaultObjectStateReducer()
|
||||
|
||||
lateinit var dataViewSubscriptionContainer: DataViewSubscriptionContainer
|
||||
|
@ -165,6 +171,9 @@ open class ObjectSetViewModelTestSetup {
|
|||
|
||||
val urlBuilder: UrlBuilder get() = UrlBuilder(gateway)
|
||||
|
||||
val defaultObjectPageType = MockDataFactory.randomString()
|
||||
val defaultObjectPageTypeName = MockDataFactory.randomString()
|
||||
|
||||
lateinit var dispatchers: AppCoroutineDispatchers
|
||||
|
||||
fun givenViewModel(): ObjectSetViewModel {
|
||||
|
@ -210,7 +219,9 @@ open class ObjectSetViewModelTestSetup {
|
|||
addObjectToCollection = addObjectToCollection,
|
||||
objectToCollection = objectToCollection,
|
||||
setQueryToObjectSet = setQueryToObjectSet,
|
||||
storeOfObjectTypes = storeOfObjectTypes
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
getDefaultPageType = getDefaultPageType,
|
||||
getTemplates = getTemplates
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -262,25 +273,6 @@ open class ObjectSetViewModelTestSetup {
|
|||
}
|
||||
}
|
||||
|
||||
fun stubGetTemplates(
|
||||
type: String,
|
||||
templates: List<Id> = emptyList()
|
||||
) {
|
||||
getTemplates.stub {
|
||||
onBlocking {
|
||||
run(
|
||||
GetTemplates.Params(type)
|
||||
)
|
||||
} doReturn templates.map {
|
||||
ObjectWrapper.Basic(
|
||||
map = mapOf(
|
||||
Relations.ID to it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun stubWorkspaceManager(workspace: Id) {
|
||||
workspaceManager.setCurrentWorkspace(workspace)
|
||||
}
|
||||
|
@ -353,9 +345,27 @@ open class ObjectSetViewModelTestSetup {
|
|||
)
|
||||
}
|
||||
|
||||
fun stubStoreOfObjectTypes() {
|
||||
fun stubStoreOfObjectTypes(map: Map<String, Any?> = emptyMap()) {
|
||||
storeOfObjectTypes.stub {
|
||||
onBlocking { get(any()) } doReturn ObjectWrapper.Type(map = emptyMap())
|
||||
onBlocking { get(any()) } doReturn ObjectWrapper.Type(map = map)
|
||||
}
|
||||
}
|
||||
|
||||
fun stubGetDefaultPageType(type: String = defaultObjectPageType, name: String = defaultObjectPageTypeName) {
|
||||
getDefaultPageType.stub {
|
||||
onBlocking { run(Unit) } doReturn GetDefaultPageType.Response(type = type, name = name)
|
||||
}
|
||||
}
|
||||
|
||||
fun stubGetTemplates(
|
||||
type: String = MockDataFactory.randomString(),
|
||||
templates: List<ObjectWrapper.Basic> = emptyList()
|
||||
) {
|
||||
val params = GetTemplates.Params(
|
||||
type = type
|
||||
)
|
||||
getTemplates.stub {
|
||||
onBlocking { async(params) }.thenReturn(Resultat.success(templates))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,7 +48,6 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() {
|
|||
doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
|
||||
details = mockObjectSet.details
|
||||
)
|
||||
stubGetTemplates(type = mockObjectSet.setOf)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockObjectSet.subscriptionId,
|
||||
workspace = mockObjectSet.workspaceId,
|
||||
|
@ -61,7 +60,8 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() {
|
|||
doReturn(Unit).`when`(createDataViewObject).async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = mockObjectSet.setOf,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -76,14 +76,15 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() {
|
|||
val second = awaitItem()
|
||||
assertIs<DataViewViewState.Set.Default>(second)
|
||||
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
advanceUntilIdle()
|
||||
verifyBlocking(createDataViewObject, times(1)) {
|
||||
async(
|
||||
CreateDataViewObject.Params.SetByType(
|
||||
type = mockObjectSet.setOf,
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -101,7 +102,6 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() {
|
|||
doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView),
|
||||
details = mockObjectSet.detailsSetByRelation
|
||||
)
|
||||
stubGetTemplates(type = mockObjectSet.setOf)
|
||||
stubSubscriptionResults(
|
||||
subscription = mockObjectSet.subscriptionId,
|
||||
workspace = mockObjectSet.workspaceId,
|
||||
|
@ -114,7 +114,8 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() {
|
|||
doReturn(Unit).`when`(createDataViewObject).async(
|
||||
CreateDataViewObject.Params.SetByRelation(
|
||||
relations = listOf(mockObjectSet.relationObject3.id),
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -129,14 +130,15 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() {
|
|||
val second = awaitItem()
|
||||
assertIs<DataViewViewState.Set.Default>(second)
|
||||
|
||||
viewModel.onCreateNewDataViewObject()
|
||||
viewModel.proceedWithCreatingNewDataViewObject()
|
||||
|
||||
advanceUntilIdle()
|
||||
verifyBlocking(createDataViewObject, times(1)) {
|
||||
async(
|
||||
CreateDataViewObject.Params.SetByRelation(
|
||||
relations = listOf(mockObjectSet.relationObject3.id),
|
||||
filters = mockObjectSet.filters
|
||||
filters = mockObjectSet.filters,
|
||||
template = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -37,6 +37,27 @@
|
|||
app:layout_constraintStart_toStartOf="@+id/textView21"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button8" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView26"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="XSmall button with arrow"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button13" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.views.ButtonPrimarySmallIcon
|
||||
android:id="@+id/btnSmallArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:clickable="true"
|
||||
app:buttonTitle="New"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView26"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView22"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -45,7 +66,7 @@
|
|||
android:text="Small Button Primary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button13" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/btnSmallArrow" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.views.ButtonPrimarySmall
|
||||
android:id="@+id/button9"
|
||||
|
|
|
@ -94,6 +94,6 @@
|
|||
android:text="Dialogs Compose"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/button20" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/btnCompose" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -62,7 +62,8 @@ fun StubObjectType(
|
|||
iconEmoji: String? = null,
|
||||
isReadOnly: Boolean? = null,
|
||||
isHidden: Boolean? = null,
|
||||
sourceObject: Id? = null
|
||||
sourceObject: Id? = null,
|
||||
recommendedLayout: Double? = null
|
||||
): ObjectWrapper.Type = ObjectWrapper.Type(
|
||||
map = mapOf(
|
||||
Relations.ID to id,
|
||||
|
@ -76,6 +77,7 @@ fun StubObjectType(
|
|||
Relations.ICON_EMOJI to iconEmoji,
|
||||
Relations.IS_READ_ONLY to isReadOnly,
|
||||
Relations.IS_HIDDEN to isHidden,
|
||||
Relations.SOURCE_OBJECT to sourceObject
|
||||
Relations.SOURCE_OBJECT to sourceObject,
|
||||
Relations.RECOMMENDED_LAYOUT to recommendedLayout
|
||||
)
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue