DROID-2121 Relations | Tags & status screen logic (#817)
This commit is contained in:
parent
6b7d95b9e7
commit
d4d1b3ea3e
|
@ -8,7 +8,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||||
import com.anytypeio.anytype.domain.objects.options.GetOptions
|
import com.anytypeio.anytype.domain.objects.options.GetOptions
|
||||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
import com.anytypeio.anytype.presentation.editor.Editor
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||||
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModel
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModel
|
||||||
|
@ -46,7 +46,7 @@ object TagStatusObjectModule {
|
||||||
fun provideFactory(
|
fun provideFactory(
|
||||||
@Named(ObjectRelationProvider.INTRINSIC_PROVIDER_TYPE) relations: ObjectRelationProvider,
|
@Named(ObjectRelationProvider.INTRINSIC_PROVIDER_TYPE) relations: ObjectRelationProvider,
|
||||||
@Named(ObjectRelationProvider.INTRINSIC_PROVIDER_TYPE) values: ObjectValueProvider,
|
@Named(ObjectRelationProvider.INTRINSIC_PROVIDER_TYPE) values: ObjectValueProvider,
|
||||||
details: ObjectDetailProvider,
|
storage: Editor.Storage,
|
||||||
storeOfObjectTypes: StoreOfObjectTypes,
|
storeOfObjectTypes: StoreOfObjectTypes,
|
||||||
urlBuilder: UrlBuilder,
|
urlBuilder: UrlBuilder,
|
||||||
setObjectDetails: UpdateDetail,
|
setObjectDetails: UpdateDetail,
|
||||||
|
@ -58,7 +58,7 @@ object TagStatusObjectModule {
|
||||||
): TagStatusViewModelFactory = TagStatusViewModelFactory(
|
): TagStatusViewModelFactory = TagStatusViewModelFactory(
|
||||||
params = params,
|
params = params,
|
||||||
values = values,
|
values = values,
|
||||||
details = details,
|
storage = storage,
|
||||||
relations = relations,
|
relations = relations,
|
||||||
storeOfObjectTypes = storeOfObjectTypes,
|
storeOfObjectTypes = storeOfObjectTypes,
|
||||||
urlBuilder = urlBuilder,
|
urlBuilder = urlBuilder,
|
||||||
|
|
|
@ -151,6 +151,7 @@ import com.anytypeio.anytype.ui.relations.RelationAddToObjectBlockFragment
|
||||||
import com.anytypeio.anytype.ui.relations.RelationDateValueFragment
|
import com.anytypeio.anytype.ui.relations.RelationDateValueFragment
|
||||||
import com.anytypeio.anytype.ui.relations.RelationTextValueFragment
|
import com.anytypeio.anytype.ui.relations.RelationTextValueFragment
|
||||||
import com.anytypeio.anytype.ui.relations.RelationValueFragment
|
import com.anytypeio.anytype.ui.relations.RelationValueFragment
|
||||||
|
import com.anytypeio.anytype.ui.relations.value.TagStatusFragment
|
||||||
import com.anytypeio.anytype.ui.spaces.SelectSpaceFragment
|
import com.anytypeio.anytype.ui.spaces.SelectSpaceFragment
|
||||||
import com.anytypeio.anytype.ui.templates.EditorTemplateFragment.Companion.ARG_TEMPLATE_ID
|
import com.anytypeio.anytype.ui.templates.EditorTemplateFragment.Companion.ARG_TEMPLATE_ID
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
|
|
@ -4,19 +4,27 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||||
|
import androidx.compose.ui.res.colorResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.anytypeio.anytype.R
|
||||||
import com.anytypeio.anytype.core_models.Id
|
import com.anytypeio.anytype.core_models.Id
|
||||||
import com.anytypeio.anytype.core_models.Key
|
import com.anytypeio.anytype.core_models.Key
|
||||||
import com.anytypeio.anytype.core_ui.relations.RelationsValueScreen
|
import com.anytypeio.anytype.core_ui.relations.RelationsValueScreen
|
||||||
|
import com.anytypeio.anytype.core_utils.ext.argBoolean
|
||||||
import com.anytypeio.anytype.core_utils.ext.argString
|
import com.anytypeio.anytype.core_utils.ext.argString
|
||||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||||
import com.anytypeio.anytype.di.common.componentManager
|
import com.anytypeio.anytype.di.common.componentManager
|
||||||
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationContext
|
||||||
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModel
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModel
|
||||||
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModelFactory
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModelFactory
|
||||||
|
import com.anytypeio.anytype.ui.settings.typography
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TagStatusFragment : BaseBottomSheetComposeFragment() {
|
class TagStatusFragment : BaseBottomSheetComposeFragment() {
|
||||||
|
@ -28,6 +36,8 @@ class TagStatusFragment : BaseBottomSheetComposeFragment() {
|
||||||
private val ctx get() = argString(CTX_KEY)
|
private val ctx get() = argString(CTX_KEY)
|
||||||
private val relationKey get() = argString(RELATION_KEY)
|
private val relationKey get() = argString(RELATION_KEY)
|
||||||
private val objectId get() = argString(OBJECT_ID_KEY)
|
private val objectId get() = argString(OBJECT_ID_KEY)
|
||||||
|
private val isLocked get() = argBoolean(IS_LOCKED_KEY)
|
||||||
|
private val relationContext get() = requireArguments().getSerializable(RELATION_CONTEXT_KEY) as RelationContext
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -36,11 +46,24 @@ class TagStatusFragment : BaseBottomSheetComposeFragment() {
|
||||||
): View = ComposeView(requireContext()).apply {
|
): View = ComposeView(requireContext()).apply {
|
||||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||||
setContent {
|
setContent {
|
||||||
RelationsValueScreen(
|
MaterialTheme(
|
||||||
state = vm.viewState.collectAsStateWithLifecycle().value,
|
typography = typography,
|
||||||
action = vm::onAction
|
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(10.dp)),
|
||||||
)
|
colors = MaterialTheme.colors.copy(
|
||||||
|
surface = colorResource(id = R.color.context_menu_background)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
RelationsValueScreen(
|
||||||
|
state = vm.viewState.collectAsStateWithLifecycle().value,
|
||||||
|
action = vm::onAction,
|
||||||
|
onQueryChanged = vm::onQueryChanged
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
vm.onStart()
|
vm.onStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +71,9 @@ class TagStatusFragment : BaseBottomSheetComposeFragment() {
|
||||||
val params = TagStatusViewModel.Params(
|
val params = TagStatusViewModel.Params(
|
||||||
ctx = ctx,
|
ctx = ctx,
|
||||||
objectId = objectId,
|
objectId = objectId,
|
||||||
relationKey = relationKey
|
relationKey = relationKey,
|
||||||
|
isLocked = isLocked,
|
||||||
|
relationContext = relationContext
|
||||||
)
|
)
|
||||||
componentManager()
|
componentManager()
|
||||||
.tagStatusObjectComponent.get(params)
|
.tagStatusObjectComponent.get(params)
|
||||||
|
@ -63,17 +88,23 @@ class TagStatusFragment : BaseBottomSheetComposeFragment() {
|
||||||
fun new(
|
fun new(
|
||||||
ctx: Id,
|
ctx: Id,
|
||||||
objectId: Id,
|
objectId: Id,
|
||||||
relationKey: Key
|
relationKey: Key,
|
||||||
|
isLocked: Boolean,
|
||||||
|
relationContext: RelationContext
|
||||||
) = TagStatusFragment().apply {
|
) = TagStatusFragment().apply {
|
||||||
arguments = bundleOf(
|
arguments = bundleOf(
|
||||||
CTX_KEY to ctx,
|
CTX_KEY to ctx,
|
||||||
OBJECT_ID_KEY to objectId,
|
OBJECT_ID_KEY to objectId,
|
||||||
RELATION_KEY to relationKey
|
RELATION_KEY to relationKey,
|
||||||
|
IS_LOCKED_KEY to isLocked,
|
||||||
|
RELATION_CONTEXT_KEY to relationContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const val CTX_KEY = "arg.tag-status.ctx"
|
const val CTX_KEY = "arg.tag-status.ctx"
|
||||||
const val RELATION_KEY = "arg.tag-status.relation.key"
|
const val RELATION_KEY = "arg.tag-status.relation.key"
|
||||||
const val OBJECT_ID_KEY = "arg.tag-status.object"
|
const val OBJECT_ID_KEY = "arg.tag-status.object"
|
||||||
|
const val IS_LOCKED_KEY = "arg.tag-status.is-locked"
|
||||||
|
const val RELATION_CONTEXT_KEY = "arg.tag-status.relation-context"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,9 +22,11 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
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.R
|
||||||
|
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
|
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
|
||||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||||
import com.anytypeio.anytype.presentation.relations.model.RelationsListItem
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationsListItem
|
||||||
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusAction
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CommonContainer(modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit) {
|
fun CommonContainer(modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit) {
|
||||||
|
@ -81,8 +83,10 @@ fun CheckedIcon(isSelected: Boolean, modifier: Modifier) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ItemTagOrStatusCreate(state: RelationsListItem.CreateItem) {
|
fun ItemTagOrStatusCreate(state: RelationsListItem.CreateItem, action: (TagStatusAction) -> Unit) {
|
||||||
CommonContainer(modifier = Modifier.padding(top = 8.dp)) {
|
CommonContainer(modifier = Modifier
|
||||||
|
.padding(top = 8.dp)
|
||||||
|
.noRippleClickable { action(TagStatusAction.Click(state)) }) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.anytypeio.anytype.core_ui.relations
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material.DropdownMenu
|
||||||
|
import androidx.compose.material.DropdownMenuItem
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.colorResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.anytypeio.anytype.core_ui.R
|
||||||
|
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||||
|
import com.anytypeio.anytype.core_ui.views.BodyCallout
|
||||||
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationsListItem
|
||||||
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusAction
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemMenu(
|
||||||
|
item: RelationsListItem.Item?,
|
||||||
|
action: (TagStatusAction) -> Unit,
|
||||||
|
isMenuExpanded: MutableState<Boolean>
|
||||||
|
) {
|
||||||
|
if (item != null) {
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = isMenuExpanded.value,
|
||||||
|
onDismissRequest = { isMenuExpanded.value = false },
|
||||||
|
modifier = Modifier.width(220.dp)
|
||||||
|
) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 11.dp),
|
||||||
|
onClick = {
|
||||||
|
isMenuExpanded.value = false
|
||||||
|
action(TagStatusAction.Edit(item.optionId))
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.edit),
|
||||||
|
style = BodyCallout,
|
||||||
|
color = colorResource(id = R.color.text_primary),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Divider(paddingEnd = 0.dp, paddingStart = 0.dp)
|
||||||
|
DropdownMenuItem(
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 11.dp),
|
||||||
|
onClick = {
|
||||||
|
isMenuExpanded.value = false
|
||||||
|
action(TagStatusAction.Duplicate(item.optionId))
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.duplicate),
|
||||||
|
style = BodyCallout,
|
||||||
|
color = colorResource(id = R.color.text_primary),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Divider(paddingEnd = 0.dp, paddingStart = 0.dp)
|
||||||
|
DropdownMenuItem(
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 11.dp),
|
||||||
|
onClick = {
|
||||||
|
isMenuExpanded.value = false
|
||||||
|
action(TagStatusAction.Delete(item.optionId))
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.delete),
|
||||||
|
style = BodyCallout,
|
||||||
|
color = colorResource(id = R.color.palette_system_red),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.anytypeio.anytype.core_models.ThemeColor
|
||||||
import com.anytypeio.anytype.core_ui.R
|
import com.anytypeio.anytype.core_ui.R
|
||||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||||
|
@ -35,14 +36,15 @@ import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||||
import com.anytypeio.anytype.core_ui.views.Title1
|
import com.anytypeio.anytype.core_ui.views.Title1
|
||||||
import com.anytypeio.anytype.core_ui.views.UXBody
|
import com.anytypeio.anytype.core_ui.views.UXBody
|
||||||
import com.anytypeio.anytype.core_ui.widgets.SearchField
|
import com.anytypeio.anytype.core_ui.widgets.SearchField
|
||||||
import com.anytypeio.anytype.presentation.relations.RelationValueView
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationsListItem
|
||||||
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusAction
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusAction
|
||||||
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewState
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RelationsValueScreen(
|
fun RelationsValueScreen(
|
||||||
state: TagStatusViewState,
|
state: TagStatusViewState,
|
||||||
action: (TagStatusAction) -> Unit
|
action: (TagStatusAction) -> Unit,
|
||||||
|
onQueryChanged: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -62,7 +64,7 @@ fun RelationsValueScreen(
|
||||||
Header(state = state, action = action)
|
Header(state = state, action = action)
|
||||||
SearchField(
|
SearchField(
|
||||||
onFocused = {},
|
onFocused = {},
|
||||||
onQueryChanged = { s -> }
|
onQueryChanged = onQueryChanged
|
||||||
)
|
)
|
||||||
Divider(paddingEnd = 0.dp, paddingStart = 0.dp)
|
Divider(paddingEnd = 0.dp, paddingStart = 0.dp)
|
||||||
RelationsLazyList(state = state, action = action)
|
RelationsLazyList(state = state, action = action)
|
||||||
|
@ -170,10 +172,10 @@ fun RelationsViewContent(
|
||||||
items = state.items,
|
items = state.items,
|
||||||
itemContent = { _, item ->
|
itemContent = { _, item ->
|
||||||
when (item) {
|
when (item) {
|
||||||
// is RelationValueView.Create -> ItemTagOrStatusCreate(state = item)
|
is RelationsListItem.Item.Tag -> TagItem(item, action)
|
||||||
// is RelationValueView.Option.Status -> StatusItem(state = item)
|
is RelationsListItem.Item.Status -> StatusItem(item, action)
|
||||||
is RelationValueView.Option.Tag -> TagItem(state = item, action = action)
|
is RelationsListItem.CreateItem.Status -> ItemTagOrStatusCreate(item, action)
|
||||||
else -> TODO()
|
is RelationsListItem.CreateItem.Tag -> ItemTagOrStatusCreate(item, action)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -191,7 +193,8 @@ fun RelationsViewLoading() {
|
||||||
|
|
||||||
private fun isClearButtonVisible(state: TagStatusViewState): Boolean {
|
private fun isClearButtonVisible(state: TagStatusViewState): Boolean {
|
||||||
if (state !is TagStatusViewState.Content) return false
|
if (state !is TagStatusViewState.Content) return false
|
||||||
return state.items.any { it is RelationValueView.Option.Tag && it.isSelected } && state.isRelationEditable
|
return state.items.any { it is RelationsListItem.Item.Tag && it.isSelected
|
||||||
|
|| it is RelationsListItem.Item.Status && it.isSelected } && state.isRelationEditable
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isPlusButtonVisible(state: TagStatusViewState): Boolean {
|
private fun isPlusButtonVisible(state: TagStatusViewState): Boolean {
|
||||||
|
@ -217,26 +220,19 @@ fun MyWidgetHeader() {
|
||||||
isRelationEditable = true,
|
isRelationEditable = true,
|
||||||
title = "Tags",
|
title = "Tags",
|
||||||
items = listOf(
|
items = listOf(
|
||||||
RelationValueView.Option.Tag(
|
RelationsListItem.Item.Tag(
|
||||||
name = "Urgent",
|
name = "Urgent",
|
||||||
color = "red",
|
color = ThemeColor.RED,
|
||||||
//number = 1,
|
number = 1,
|
||||||
isSelected = true,
|
isSelected = true,
|
||||||
id = "1",
|
optionId = "1"
|
||||||
removable = false,
|
|
||||||
isCheckboxShown = false
|
|
||||||
),
|
),
|
||||||
RelationValueView.Option.Tag(
|
RelationsListItem.Item.Tag(
|
||||||
name = "Personal",
|
name = "Personal",
|
||||||
color = "orange",
|
color = ThemeColor.ORANGE,
|
||||||
//number = 1,
|
number = 2,
|
||||||
isSelected = false,
|
isSelected = false,
|
||||||
id = "1",
|
optionId = "1"
|
||||||
removable = false,
|
|
||||||
isCheckboxShown = false
|
|
||||||
),
|
|
||||||
RelationValueView.Create(
|
|
||||||
name = "Done"
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
), action = {})
|
), action = {})
|
||||||
|
|
|
@ -10,19 +10,39 @@ import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.material.Divider
|
import androidx.compose.material.Divider
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.anytypeio.anytype.core_models.ThemeColor
|
import com.anytypeio.anytype.core_models.ThemeColor
|
||||||
import com.anytypeio.anytype.core_ui.extensions.dark
|
import com.anytypeio.anytype.core_ui.extensions.dark
|
||||||
|
import com.anytypeio.anytype.core_ui.foundation.noRippleCombinedClickable
|
||||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||||
import com.anytypeio.anytype.presentation.relations.model.RelationsListItem
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationsListItem
|
||||||
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusAction
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StatusItem(state: RelationsListItem.Item.Status) {
|
fun StatusItem(
|
||||||
CommonContainer {
|
state: RelationsListItem.Item.Status,
|
||||||
|
action: (TagStatusAction) -> Unit
|
||||||
|
) {
|
||||||
|
val haptics = LocalHapticFeedback.current
|
||||||
|
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||||
|
CommonContainer(
|
||||||
|
modifier = Modifier
|
||||||
|
.noRippleCombinedClickable(
|
||||||
|
onClick = { action(TagStatusAction.Click(state)) },
|
||||||
|
onLongClicked = {
|
||||||
|
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
|
isMenuExpanded.value = !isMenuExpanded.value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
@ -38,13 +58,18 @@ fun StatusItem(state: RelationsListItem.Item.Status) {
|
||||||
.align(Alignment.CenterEnd)
|
.align(Alignment.CenterEnd)
|
||||||
)
|
)
|
||||||
Divider(modifier = Modifier.align(Alignment.BottomCenter))
|
Divider(modifier = Modifier.align(Alignment.BottomCenter))
|
||||||
|
ItemMenu(
|
||||||
|
item = state,
|
||||||
|
action = action,
|
||||||
|
isMenuExpanded = isMenuExpanded
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StatusItemText(state: RelationsListItem.Item.Status) {
|
fun StatusItemText(state: RelationsListItem.Item.Status) {
|
||||||
Text(
|
Text(
|
||||||
text = state.text,
|
text = state.name,
|
||||||
color = dark(state.color),
|
color = dark(state.color),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.wrapContentWidth()
|
.wrapContentWidth()
|
||||||
|
@ -65,22 +90,27 @@ fun PreviewStatusItem() {
|
||||||
) {
|
) {
|
||||||
StatusItem(
|
StatusItem(
|
||||||
state = RelationsListItem.Item.Status(
|
state = RelationsListItem.Item.Status(
|
||||||
text = "In development",
|
optionId = "1",
|
||||||
|
name = "In development",
|
||||||
color = ThemeColor.RED,
|
color = ThemeColor.RED,
|
||||||
isSelected = true
|
isSelected = true
|
||||||
)
|
),
|
||||||
|
action = {}
|
||||||
)
|
)
|
||||||
StatusItem(
|
StatusItem(
|
||||||
state = RelationsListItem.Item.Status(
|
state = RelationsListItem.Item.Status(
|
||||||
text = "Designer",
|
optionId = "2",
|
||||||
|
name = "Designer",
|
||||||
color = ThemeColor.TEAL,
|
color = ThemeColor.TEAL,
|
||||||
isSelected = false
|
isSelected = false
|
||||||
)
|
),
|
||||||
|
action = {}
|
||||||
)
|
)
|
||||||
ItemTagOrStatusCreate(
|
ItemTagOrStatusCreate(
|
||||||
state = RelationsListItem.CreateItem.Status(
|
state = RelationsListItem.CreateItem.Status(
|
||||||
text = "Personal"
|
text = "Personal"
|
||||||
)
|
),
|
||||||
|
action = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,24 +12,39 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.Divider
|
import androidx.compose.material.Divider
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.anytypeio.anytype.core_models.ThemeColor
|
import com.anytypeio.anytype.core_models.ThemeColor
|
||||||
import com.anytypeio.anytype.core_ui.extensions.dark
|
import com.anytypeio.anytype.core_ui.extensions.dark
|
||||||
import com.anytypeio.anytype.core_ui.extensions.light
|
import com.anytypeio.anytype.core_ui.extensions.light
|
||||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
import com.anytypeio.anytype.core_ui.foundation.noRippleCombinedClickable
|
||||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||||
import com.anytypeio.anytype.presentation.relations.RelationValueView
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationsListItem
|
||||||
import com.anytypeio.anytype.presentation.relations.model.RelationsListItem
|
|
||||||
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusAction
|
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusAction
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TagItem(state: RelationValueView.Option.Tag, action: (TagStatusAction) -> Unit) {
|
fun TagItem(
|
||||||
|
state: RelationsListItem.Item.Tag,
|
||||||
|
action: (TagStatusAction) -> Unit
|
||||||
|
) {
|
||||||
|
val haptics = LocalHapticFeedback.current
|
||||||
|
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||||
CommonContainer(
|
CommonContainer(
|
||||||
modifier = Modifier.noRippleClickable { action(TagStatusAction.Click(state)) }
|
modifier = Modifier
|
||||||
|
.noRippleCombinedClickable(
|
||||||
|
onClick = { action(TagStatusAction.Click(state)) },
|
||||||
|
onLongClicked = {
|
||||||
|
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
|
isMenuExpanded.value = !isMenuExpanded.value
|
||||||
|
}
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -40,26 +55,30 @@ fun TagItem(state: RelationValueView.Option.Tag, action: (TagStatusAction) -> Un
|
||||||
TagItemText(state = state)
|
TagItemText(state = state)
|
||||||
}
|
}
|
||||||
CircleIcon(
|
CircleIcon(
|
||||||
//number = if (state.isSelected) state.number.toString() else null,
|
number = if (state.isSelected) state.number.toString() else null,
|
||||||
isSelected = state.isSelected,
|
isSelected = state.isSelected,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
.size(24.dp)
|
||||||
.align(Alignment.CenterEnd)
|
.align(Alignment.CenterEnd)
|
||||||
)
|
)
|
||||||
Divider(modifier = Modifier.align(Alignment.BottomCenter))
|
Divider(modifier = Modifier.align(Alignment.BottomCenter))
|
||||||
|
ItemMenu(
|
||||||
|
item = state,
|
||||||
|
action = action,
|
||||||
|
isMenuExpanded = isMenuExpanded
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TagItemText(state: RelationValueView.Option.Tag) {
|
fun TagItemText(state: RelationsListItem.Item.Tag) {
|
||||||
val themeColor = ThemeColor.values().find { it.code == state.color } ?: ThemeColor.DEFAULT
|
|
||||||
Text(
|
Text(
|
||||||
text = state.name,
|
text = state.name,
|
||||||
color = dark(themeColor),
|
color = dark(state.color),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.wrapContentWidth()
|
.wrapContentWidth()
|
||||||
.background(
|
.background(
|
||||||
color = light(color = themeColor),
|
color = light(color = state.color),
|
||||||
shape = RoundedCornerShape(size = 3.dp)
|
shape = RoundedCornerShape(size = 3.dp)
|
||||||
)
|
)
|
||||||
.padding(start = 6.dp, end = 6.dp),
|
.padding(start = 6.dp, end = 6.dp),
|
||||||
|
@ -78,33 +97,30 @@ fun PreviewTagItem() {
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
) {
|
) {
|
||||||
TagItem(
|
TagItem(
|
||||||
state = RelationValueView.Option.Tag(
|
state = RelationsListItem.Item.Tag(
|
||||||
name = "Urgent",
|
name = "Urgent",
|
||||||
color = "red",
|
color = ThemeColor.RED,
|
||||||
//number = 1,
|
number = 1,
|
||||||
isSelected = true,
|
isSelected = true,
|
||||||
id = "1",
|
optionId = "1"
|
||||||
removable = false,
|
|
||||||
isCheckboxShown = false
|
|
||||||
),
|
),
|
||||||
action = {}
|
action = {}
|
||||||
)
|
)
|
||||||
TagItem(
|
TagItem(
|
||||||
state = RelationValueView.Option.Tag(
|
state = RelationsListItem.Item.Tag(
|
||||||
name = "Personal",
|
name = "Personal",
|
||||||
color = "orange",
|
color = ThemeColor.ORANGE,
|
||||||
//number = 1,
|
number = 1,
|
||||||
isSelected = true,
|
isSelected = true,
|
||||||
id = "1",
|
optionId = "1"
|
||||||
removable = false,
|
|
||||||
isCheckboxShown = false
|
|
||||||
),
|
),
|
||||||
action = {}
|
action = {}
|
||||||
)
|
)
|
||||||
ItemTagOrStatusCreate(
|
ItemTagOrStatusCreate(
|
||||||
state = RelationsListItem.CreateItem.Tag(
|
state = RelationsListItem.CreateItem.Tag(
|
||||||
text = "Done"
|
text = "Done"
|
||||||
)
|
),
|
||||||
|
action = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -63,11 +63,11 @@ class GetOptions(
|
||||||
Relations.NAME,
|
Relations.NAME,
|
||||||
Relations.RELATION_OPTION_COLOR
|
Relations.RELATION_OPTION_COLOR
|
||||||
),
|
),
|
||||||
fulltext = "",
|
fulltext = params.fulltext,
|
||||||
).map {
|
).map {
|
||||||
ObjectWrapper.Option(it)
|
ObjectWrapper.Option(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Params(val space: Id, val relation: Key)
|
data class Params(val space: Id, val relation: Key, val fulltext: String = "")
|
||||||
}
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
package com.anytypeio.anytype.presentation.relations.model
|
|
||||||
|
|
||||||
import com.anytypeio.anytype.core_models.ThemeColor
|
|
||||||
|
|
||||||
sealed class RelationsListItem {
|
|
||||||
|
|
||||||
abstract val text: String
|
|
||||||
|
|
||||||
sealed class Item : RelationsListItem() {
|
|
||||||
data class Tag(
|
|
||||||
override val text: String,
|
|
||||||
val color: ThemeColor,
|
|
||||||
val isSelected: Boolean,
|
|
||||||
val number: Int? = null
|
|
||||||
) : Item()
|
|
||||||
|
|
||||||
data class Status(
|
|
||||||
override val text: String,
|
|
||||||
val color: ThemeColor,
|
|
||||||
val isSelected: Boolean
|
|
||||||
) : Item()
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class CreateItem(
|
|
||||||
override val text: String
|
|
||||||
) : RelationsListItem() {
|
|
||||||
class Tag(text: String) : CreateItem(text)
|
|
||||||
class Status(text: String) : CreateItem(text)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +1,40 @@
|
||||||
package com.anytypeio.anytype.presentation.relations.value.tagstatus
|
package com.anytypeio.anytype.presentation.relations.value.tagstatus
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.anytypeio.anytype.analytics.base.Analytics
|
import com.anytypeio.anytype.analytics.base.Analytics
|
||||||
import com.anytypeio.anytype.core_models.Id
|
import com.anytypeio.anytype.core_models.Id
|
||||||
|
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||||
import com.anytypeio.anytype.core_models.Payload
|
import com.anytypeio.anytype.core_models.Payload
|
||||||
|
import com.anytypeio.anytype.core_models.Relation
|
||||||
|
import com.anytypeio.anytype.core_models.Struct
|
||||||
|
import com.anytypeio.anytype.core_models.ThemeColor
|
||||||
|
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||||
|
import com.anytypeio.anytype.core_utils.ext.typeOf
|
||||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||||
import com.anytypeio.anytype.domain.objects.options.GetOptions
|
import com.anytypeio.anytype.domain.objects.options.GetOptions
|
||||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||||
import com.anytypeio.anytype.presentation.relations.RelationValueView
|
import com.anytypeio.anytype.presentation.editor.Editor
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||||
|
import com.anytypeio.anytype.presentation.sets.filterIdsById
|
||||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class TagStatusViewModel(
|
class TagStatusViewModel(
|
||||||
private val params: Params,
|
private val params: Params,
|
||||||
private val relations: ObjectRelationProvider,
|
private val relations: ObjectRelationProvider,
|
||||||
private val values: ObjectValueProvider,
|
private val values: ObjectValueProvider,
|
||||||
private val details: ObjectDetailProvider,
|
private val storage: Editor.Storage,
|
||||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||||
private val urlBuilder: UrlBuilder,
|
private val urlBuilder: UrlBuilder,
|
||||||
private val dispatcher: Dispatcher<Payload>,
|
private val dispatcher: Dispatcher<Payload>,
|
||||||
|
@ -32,19 +45,298 @@ class TagStatusViewModel(
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
val viewState = MutableStateFlow<TagStatusViewState>(TagStatusViewState.Loading)
|
val viewState = MutableStateFlow<TagStatusViewState>(TagStatusViewState.Loading)
|
||||||
|
private val query = MutableSharedFlow<String>()
|
||||||
|
private var isRelationNotEditable = false
|
||||||
|
|
||||||
fun onStart() {
|
fun onStart() {
|
||||||
Timber.d("TagStatusViewModel onStart, params: $params")
|
val obj = storage.details.current().details[params.objectId]
|
||||||
|
isRelationNotEditable = params.isLocked || storage.objectRestrictions.current()
|
||||||
|
.contains(ObjectRestriction.RELATIONS)
|
||||||
|
Timber.d("TagStatusViewModel onStart, params: $params, obj: $obj, isRelationNotEditable: $isRelationNotEditable")
|
||||||
|
viewModelScope.launch {
|
||||||
|
combine(
|
||||||
|
relations.observe(
|
||||||
|
relation = params.relationKey
|
||||||
|
),
|
||||||
|
values.subscribe(
|
||||||
|
ctx = params.ctx,
|
||||||
|
target = params.objectId
|
||||||
|
),
|
||||||
|
query.onStart { emit("") }
|
||||||
|
) { relation, record, query ->
|
||||||
|
setupIsRelationNotEditable(relation)
|
||||||
|
getAllOptions(
|
||||||
|
relation = relation,
|
||||||
|
record = record,
|
||||||
|
query = query
|
||||||
|
)
|
||||||
|
}.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onQueryChanged(input: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
query.emit(input)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onAction(action: TagStatusAction) {
|
fun onAction(action: TagStatusAction) {
|
||||||
Timber.d("TagStatusViewModel onAction, action: $action")
|
Timber.d("TagStatusViewModel onAction, action: $action")
|
||||||
|
if (isRelationNotEditable) {
|
||||||
|
Timber.d("TagStatusViewModel onAction, relation is not editable")
|
||||||
|
sendToast("Relation is not editable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
when (action) {
|
||||||
|
TagStatusAction.Clear -> clearTagsOrStatus()
|
||||||
|
is TagStatusAction.Click -> onActionClick(action.item)
|
||||||
|
is TagStatusAction.LongClick -> {
|
||||||
|
val currentState = viewState.value
|
||||||
|
if (currentState is TagStatusViewState.Content) {
|
||||||
|
viewState.value = currentState.copy(showItemMenu = action.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TagStatusAction.Plus -> TODO()
|
||||||
|
is TagStatusAction.Delete -> TODO()
|
||||||
|
is TagStatusAction.Duplicate -> TODO()
|
||||||
|
is TagStatusAction.Edit -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onActionClick(item: RelationsListItem) {
|
||||||
|
when (item) {
|
||||||
|
is RelationsListItem.Item.Status -> {
|
||||||
|
if (item.isSelected) {
|
||||||
|
clearTagsOrStatus()
|
||||||
|
} else {
|
||||||
|
addStatus(item.optionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RelationsListItem.Item.Tag -> {
|
||||||
|
if (item.isSelected) {
|
||||||
|
removeTag(item.optionId)
|
||||||
|
} else {
|
||||||
|
addTag(item.optionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RelationsListItem.CreateItem.Status -> TODO()
|
||||||
|
is RelationsListItem.CreateItem.Tag -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getAllOptions(
|
||||||
|
relation: ObjectWrapper.Relation,
|
||||||
|
record: Struct,
|
||||||
|
query: String
|
||||||
|
) {
|
||||||
|
val params = GetOptions.Params(
|
||||||
|
space = spaceManager.get(),
|
||||||
|
relation = relation.key,
|
||||||
|
fulltext = query
|
||||||
|
)
|
||||||
|
getOptions(params).proceed(
|
||||||
|
success = { options ->
|
||||||
|
Timber.d("TagStatusViewModel getAllOptions, options: ${options.size}")
|
||||||
|
initViewState(
|
||||||
|
relation = relation,
|
||||||
|
record = record,
|
||||||
|
options = options,
|
||||||
|
query = query
|
||||||
|
)
|
||||||
|
},
|
||||||
|
failure = {
|
||||||
|
Timber.e(it, "Error while getting options by id")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initViewState(
|
||||||
|
relation: ObjectWrapper.Relation,
|
||||||
|
record: Map<String, Any?>,
|
||||||
|
options: List<ObjectWrapper.Option>,
|
||||||
|
query: String
|
||||||
|
) {
|
||||||
|
val result = mutableListOf<RelationsListItem>()
|
||||||
|
when (relation.format) {
|
||||||
|
Relation.Format.STATUS -> {
|
||||||
|
val ids: List<Id> = when (val value = record[params.relationKey]) {
|
||||||
|
is Id -> listOf(value)
|
||||||
|
is List<*> -> value.typeOf()
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
result.addAll(
|
||||||
|
mapStatusOptions(
|
||||||
|
ids = ids,
|
||||||
|
options = options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Relation.Format.TAG -> {
|
||||||
|
val ids: List<Id> = when (val value = record[params.relationKey]) {
|
||||||
|
is Id -> listOf(value)
|
||||||
|
is List<*> -> value.typeOf()
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
result.addAll(
|
||||||
|
mapTagOptions(
|
||||||
|
ids = ids,
|
||||||
|
options = options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (query.isNotBlank()) {
|
||||||
|
result.add(RelationsListItem.CreateItem.Tag(query))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Timber.w("Relation format should be Tag or Status but was: ${relation.format}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewState.value = if (result.isEmpty()) {
|
||||||
|
TagStatusViewState.Empty(
|
||||||
|
isRelationEditable = !isRelationNotEditable,
|
||||||
|
title = relation.name.orEmpty(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
TagStatusViewState.Content(
|
||||||
|
isRelationEditable = !isRelationNotEditable,
|
||||||
|
title = relation.name.orEmpty(),
|
||||||
|
items = result
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addTag(tag: Id) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val obj = values.get(ctx = params.ctx, target = params.objectId)
|
||||||
|
val result = mutableListOf<Id>()
|
||||||
|
val value = obj[params.relationKey]
|
||||||
|
if (value is List<*>) {
|
||||||
|
result.addAll(value.typeOf())
|
||||||
|
} else if (value is Id) {
|
||||||
|
result.add(value)
|
||||||
|
}
|
||||||
|
result.add(tag)
|
||||||
|
setObjectDetails(
|
||||||
|
UpdateDetail.Params(
|
||||||
|
target = params.objectId,
|
||||||
|
key = params.relationKey,
|
||||||
|
value = result
|
||||||
|
)
|
||||||
|
).process(
|
||||||
|
failure = { Timber.e(it, "Error while adding tag") },
|
||||||
|
success = {
|
||||||
|
dispatcher.send(it)
|
||||||
|
sendAnalyticsRelationValueEvent(analytics)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeTag(tag: Id) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val obj = values.get(ctx = params.ctx, target = params.objectId)
|
||||||
|
val remaining = obj[params.relationKey].filterIdsById(tag)
|
||||||
|
setObjectDetails(
|
||||||
|
UpdateDetail.Params(
|
||||||
|
target = params.objectId,
|
||||||
|
key = params.relationKey,
|
||||||
|
value = remaining
|
||||||
|
)
|
||||||
|
).process(
|
||||||
|
failure = { Timber.e(it, "Error while adding tag") },
|
||||||
|
success = {
|
||||||
|
dispatcher.send(it)
|
||||||
|
sendAnalyticsRelationValueEvent(analytics)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addStatus(status: Id) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
setObjectDetails(
|
||||||
|
UpdateDetail.Params(
|
||||||
|
target = params.objectId,
|
||||||
|
key = params.relationKey,
|
||||||
|
value = listOf(status)
|
||||||
|
)
|
||||||
|
).process(
|
||||||
|
failure = { Timber.e(it, "Error while adding tag") },
|
||||||
|
success = {
|
||||||
|
dispatcher.send(it)
|
||||||
|
sendAnalyticsRelationValueEvent(analytics)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearTagsOrStatus() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
setObjectDetails(
|
||||||
|
UpdateDetail.Params(
|
||||||
|
target = params.objectId,
|
||||||
|
key = params.relationKey,
|
||||||
|
value = null
|
||||||
|
)
|
||||||
|
).process(
|
||||||
|
failure = { Timber.e(it, "Error while clearing tags or select") },
|
||||||
|
success = {
|
||||||
|
dispatcher.send(it)
|
||||||
|
sendAnalyticsRelationValueEvent(analytics)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mapTagOptions(
|
||||||
|
ids: List<Id>,
|
||||||
|
options: List<ObjectWrapper.Option>
|
||||||
|
) = options.map { option ->
|
||||||
|
val index = ids.indexOf(option.id)
|
||||||
|
val isSelected = index != -1
|
||||||
|
val number = if (isSelected) index + 1 else Int.MAX_VALUE
|
||||||
|
RelationsListItem.Item.Tag(
|
||||||
|
optionId = option.id,
|
||||||
|
name = option.name.orEmpty(),
|
||||||
|
color = getOrDefault(option.color),
|
||||||
|
isSelected = isSelected,
|
||||||
|
number = number
|
||||||
|
)
|
||||||
|
}.sortedBy { it.number }
|
||||||
|
|
||||||
|
private fun mapStatusOptions(
|
||||||
|
ids: List<Id>,
|
||||||
|
options: List<ObjectWrapper.Option>
|
||||||
|
) = options.map { option ->
|
||||||
|
val index = ids.indexOf(option.id)
|
||||||
|
val isSelected = index != -1
|
||||||
|
RelationsListItem.Item.Status(
|
||||||
|
optionId = option.id,
|
||||||
|
name = option.name.orEmpty(),
|
||||||
|
color = getOrDefault(option.color),
|
||||||
|
isSelected = isSelected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrDefault(code: String?): ThemeColor {
|
||||||
|
return ThemeColor.values().find { it.code == code } ?: ThemeColor.DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupIsRelationNotEditable(relation: ObjectWrapper.Relation) {
|
||||||
|
isRelationNotEditable = params.isLocked
|
||||||
|
|| storage.objectRestrictions.current().contains(ObjectRestriction.RELATIONS)
|
||||||
|
|| relation.isReadonlyValue
|
||||||
|
|| relation.isHidden == true
|
||||||
|
|| relation.isDeleted == true
|
||||||
|
|| relation.isArchived == true
|
||||||
|
|| !relation.isValid
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
val ctx: Id,
|
val ctx: Id,
|
||||||
val objectId: Id,
|
val objectId: Id,
|
||||||
val relationKey: Id
|
val relationKey: Id,
|
||||||
|
val isLocked: Boolean,
|
||||||
|
val relationContext: RelationContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,14 +348,50 @@ sealed class TagStatusViewState {
|
||||||
|
|
||||||
data class Content(
|
data class Content(
|
||||||
val title: String,
|
val title: String,
|
||||||
val items: List<RelationValueView>,
|
val items: List<RelationsListItem>,
|
||||||
val isRelationEditable: Boolean
|
val isRelationEditable: Boolean,
|
||||||
|
val showItemMenu: RelationsListItem.Item? = null
|
||||||
) : TagStatusViewState()
|
) : TagStatusViewState()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class TagStatusAction {
|
sealed class TagStatusAction {
|
||||||
data class Click(val item: RelationValueView) : TagStatusAction()
|
data class Click(val item: RelationsListItem) : TagStatusAction()
|
||||||
data class LongClick(val item: RelationValueView) : TagStatusAction()
|
data class LongClick(val item: RelationsListItem.Item) : TagStatusAction()
|
||||||
object Clear : TagStatusAction()
|
object Clear : TagStatusAction()
|
||||||
object Plus : TagStatusAction()
|
object Plus : TagStatusAction()
|
||||||
|
data class Edit(val optionId: Id) : TagStatusAction()
|
||||||
|
data class Delete(val optionId: Id) : TagStatusAction()
|
||||||
|
data class Duplicate(val optionId: Id) : TagStatusAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RelationContext{ OBJECT, OBJECT_SET, DATA_VIEW }
|
||||||
|
|
||||||
|
sealed class RelationsListItem {
|
||||||
|
|
||||||
|
sealed class Item : RelationsListItem() {
|
||||||
|
|
||||||
|
abstract val optionId: Id
|
||||||
|
data class Tag(
|
||||||
|
override val optionId: Id,
|
||||||
|
val name: String,
|
||||||
|
val color: ThemeColor,
|
||||||
|
val isSelected: Boolean,
|
||||||
|
val number: Int = Int.MAX_VALUE,
|
||||||
|
val showMenu: Boolean = false
|
||||||
|
) : Item()
|
||||||
|
|
||||||
|
data class Status(
|
||||||
|
override val optionId: Id,
|
||||||
|
val name: String,
|
||||||
|
val color: ThemeColor,
|
||||||
|
val isSelected: Boolean
|
||||||
|
) : Item()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class CreateItem(
|
||||||
|
val text: String
|
||||||
|
) : RelationsListItem() {
|
||||||
|
class Tag(text: String) : CreateItem(text)
|
||||||
|
class Status(text: String) : CreateItem(text)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||||
import com.anytypeio.anytype.domain.objects.options.GetOptions
|
import com.anytypeio.anytype.domain.objects.options.GetOptions
|
||||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
import com.anytypeio.anytype.presentation.editor.Editor
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
||||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||||
|
@ -19,7 +19,7 @@ class TagStatusViewModelFactory @Inject constructor(
|
||||||
private val params: TagStatusViewModel.Params,
|
private val params: TagStatusViewModel.Params,
|
||||||
private val relations: ObjectRelationProvider,
|
private val relations: ObjectRelationProvider,
|
||||||
private val values: ObjectValueProvider,
|
private val values: ObjectValueProvider,
|
||||||
private val details: ObjectDetailProvider,
|
private val storage: Editor.Storage,
|
||||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||||
private val urlBuilder: UrlBuilder,
|
private val urlBuilder: UrlBuilder,
|
||||||
private val dispatcher: Dispatcher<Payload>,
|
private val dispatcher: Dispatcher<Payload>,
|
||||||
|
@ -35,7 +35,7 @@ class TagStatusViewModelFactory @Inject constructor(
|
||||||
params = params,
|
params = params,
|
||||||
relations = relations,
|
relations = relations,
|
||||||
values = values,
|
values = values,
|
||||||
details = details,
|
storage = storage,
|
||||||
storeOfObjectTypes = storeOfObjectTypes,
|
storeOfObjectTypes = storeOfObjectTypes,
|
||||||
dispatcher = dispatcher,
|
dispatcher = dispatcher,
|
||||||
setObjectDetails = setObjectDetails,
|
setObjectDetails = setObjectDetails,
|
||||||
|
|
Loading…
Reference in New Issue