Compare commits

...

29 Commits

Author SHA1 Message Date
konstantiniiv
3975cebe0e DROID-2121 create tag, status 2024-02-03 13:15:28 +01:00
konstantiniiv
3ed09113fd DROID-2121 rename 2024-02-03 13:08:39 +01:00
konstantiniiv
cbcc02656c DROID-2121 options search constants 2024-02-03 13:06:31 +01:00
konstantiniiv
50cdb9493b DROID-2121 option subscribtion 2024-02-03 13:05:51 +01:00
konstantiniiv
63e9a1dc2b DROID-2121 fix 2024-02-03 12:29:15 +01:00
konstantiniiv
61026f38f3 DROID-2121 tag status screen update 2024-02-02 23:34:11 +01:00
konstantiniiv
0985632e4c DROID-2121 option logic 2024-02-02 23:33:55 +01:00
konstantiniiv
0a9b134a55 DROID-2121 tag status screen 2024-02-02 16:18:12 +01:00
konstantiniiv
cdd4085161 DROID-2121 fic 2024-02-02 16:17:57 +01:00
konstantiniiv
f17d8fe601 DROID-2121 option screen 2024-02-02 16:17:52 +01:00
konstantiniiv
9219d007d3 DROID-2121 option widget 2024-02-02 14:10:10 +01:00
konstantiniiv
159db9a85d DROID-2121 icon 2024-02-02 14:08:34 +01:00
konstantiniiv
de7d2b3d42 DROID-2121 commands 2024-02-01 16:05:19 +01:00
konstantiniiv
e101d11d49 DROID-2121 Merge branch 'main' into droid-2121-relations-tags-status-screen-add
# Conflicts:
#	presentation/src/main/java/com/anytypeio/anytype/presentation/relations/value/tagstatus/TagStatusViewModel.kt
2024-02-01 15:58:03 +01:00
konstantiniiv
aaecfea468 DROID-2121 options screen 2024-02-01 15:44:37 +01:00
konstantiniiv
907fbfb4c3 DROID-2121 view state + actions 2024-02-01 12:14:09 +01:00
Konstantin Ivanov
005fca7c24
Merge branch 'main' into droid-2121-relations-tags-status-screen-logic 2024-01-31 23:50:50 +01:00
konstantiniiv
2c80d0a250 DROID-2121 revert 2024-01-31 23:49:39 +01:00
konstantiniiv
f96156f10f DROID-2121 compose fixes 2024-01-31 23:26:45 +01:00
konstantiniiv
49c51d76e7 DROID-2121 item menu 2024-01-31 23:17:28 +01:00
konstantiniiv
b796c9ebb5 DROID-2121 view model 2024-01-31 16:28:27 +01:00
konstantiniiv
d82bba1647 DROID-2121 fix 2024-01-31 16:28:13 +01:00
konstantiniiv
87b2268091 DROID-2121 show new screen 2024-01-31 16:26:55 +01:00
konstantiniiv
ff6b552445 DROID-2121 update search logic 2024-01-31 14:44:50 +01:00
konstantiniiv
b9d82662b8 DROID-2121 tag and status, view model + screen 2024-01-31 14:35:09 +01:00
konstantiniiv
53e7127df8 DROID-2121 compose, search 2024-01-31 14:34:47 +01:00
konstantiniiv
bc2a6e0db1 DROID-2121 get options 2024-01-31 14:33:41 +01:00
konstantiniiv
3f9b00b35d DROID-2121 di 2024-01-31 14:33:33 +01:00
konstantiniiv
153e0a47f8 DROID-2121 view model 2024-01-31 13:24:46 +01:00
17 changed files with 1014 additions and 105 deletions

View File

@ -102,6 +102,7 @@ import com.anytypeio.anytype.di.feature.widgets.SelectWidgetSourceModule
import com.anytypeio.anytype.di.feature.widgets.SelectWidgetTypeModule
import com.anytypeio.anytype.di.main.MainComponent
import com.anytypeio.anytype.presentation.objects.SelectObjectTypeViewModel
import com.anytypeio.anytype.presentation.relations.option.OptionViewModel
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModel
import com.anytypeio.anytype.ui.relations.RelationEditParameters
import com.anytypeio.anytype.ui.types.edit.TypeEditParameters
@ -949,13 +950,20 @@ class ComponentManager(
.create(findComponentDependencies())
}
val tagStatusObjectComponent = ComponentWithParams { params: TagStatusViewModel.Params ->
val tagStatusObjectComponent = ComponentWithParams { params: TagStatusViewModel.ViewModelParams ->
editorComponent.get(params.ctx)
.tagStatusObjectComponent()
.params(params)
.build()
}
val optionObjectComponent = ComponentWithParams { params: OptionViewModel.Params ->
editorComponent.get(params.ctx)
.optionObjectComponent()
.params(params)
.build()
}
class Component<T>(private val builder: () -> T) {
private var instance: T? = null

View File

@ -0,0 +1,68 @@
package com.anytypeio.anytype.di.feature
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_utils.di.scope.PerModal
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.relations.CreateRelationOption
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.relations.option.OptionViewModel
import com.anytypeio.anytype.presentation.relations.option.OptionViewModelFactory
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.ui.relations.value.OptionFragment
import dagger.BindsInstance
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
import javax.inject.Named
@PerModal
@Subcomponent(
modules = [OptionObjectModule::class]
)
interface OptionObjectComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance
fun params(params: OptionViewModel.Params): Builder
fun build(): OptionObjectComponent
}
fun inject(fragment: OptionFragment)
}
@Module
object OptionObjectModule {
@JvmStatic
@Provides
@PerModal
fun createRelationOption(
repo: BlockRepository
): CreateRelationOption = CreateRelationOption(repo = repo)
@JvmStatic
@Provides
@PerModal
fun provideFactory(
params: OptionViewModel.Params,
@Named(ObjectRelationProvider.INTRINSIC_PROVIDER_TYPE) values: ObjectValueProvider,
setObjectDetails: SetObjectDetails,
dispatcher: Dispatcher<Payload>,
spaceManager: SpaceManager,
analytics: Analytics,
createOption: CreateRelationOption
): OptionViewModelFactory = OptionViewModelFactory(
params = params,
values = values,
createOption = createOption,
setObjectDetails = setObjectDetails,
dispatcher = dispatcher,
spaceManager = spaceManager,
analytics = analytics,
)
}

View File

@ -3,14 +3,17 @@ package com.anytypeio.anytype.di.feature
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_utils.di.scope.PerModal
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.debugging.Logger
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.options.GetOptions
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
import com.anytypeio.anytype.presentation.relations.value.tagstatus.SUB_MY_OPTIONS
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModel
import com.anytypeio.anytype.presentation.relations.value.tagstatus.TagStatusViewModelFactory
import com.anytypeio.anytype.presentation.util.Dispatcher
@ -30,7 +33,7 @@ interface TagStatusObjectComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance
fun params(params: TagStatusViewModel.Params): Builder
fun params(params: TagStatusViewModel.ViewModelParams): Builder
fun build(): TagStatusObjectComponent
}
@ -40,6 +43,22 @@ interface TagStatusObjectComponent {
@Module
object TagStatusObjectModule {
@JvmStatic
@Provides
@PerModal
@Named(SUB_MY_OPTIONS)
fun provideStoreLessSubscriptionContainer(
repo: BlockRepository,
channel: SubscriptionEventChannel,
dispatchers: AppCoroutineDispatchers,
logger: Logger
): StorelessSubscriptionContainer = StorelessSubscriptionContainer.Impl(
repo = repo,
channel = channel,
dispatchers = dispatchers,
logger = logger
)
@JvmStatic
@Provides
@PerModal
@ -47,25 +66,21 @@ object TagStatusObjectModule {
@Named(ObjectRelationProvider.INTRINSIC_PROVIDER_TYPE) relations: ObjectRelationProvider,
@Named(ObjectRelationProvider.INTRINSIC_PROVIDER_TYPE) values: ObjectValueProvider,
storage: Editor.Storage,
storeOfObjectTypes: StoreOfObjectTypes,
urlBuilder: UrlBuilder,
setObjectDetails: UpdateDetail,
dispatcher: Dispatcher<Payload>,
analytics: Analytics,
getOptions: GetOptions,
spaceManager: SpaceManager,
params: TagStatusViewModel.Params
params: TagStatusViewModel.ViewModelParams,
@Named(SUB_MY_OPTIONS) subscription: StorelessSubscriptionContainer
): TagStatusViewModelFactory = TagStatusViewModelFactory(
params = params,
values = values,
storage = storage,
relations = relations,
storeOfObjectTypes = storeOfObjectTypes,
urlBuilder = urlBuilder,
setObjectDetails = setObjectDetails,
dispatcher = dispatcher,
analytics = analytics,
getOptions = getOptions,
spaceManager = spaceManager
spaceManager = spaceManager,
subscription = subscription
)
}

View File

@ -0,0 +1,145 @@
package com.anytypeio.anytype.ui.relations.value
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
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.ViewCompositionStrategy
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_ui.relations.OptionWidget
import com.anytypeio.anytype.core_utils.ext.argString
import com.anytypeio.anytype.core_utils.ext.argStringOrNull
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.relations.option.OptionViewModel
import com.anytypeio.anytype.presentation.relations.option.OptionViewModelFactory
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationContext
import com.anytypeio.anytype.ui.settings.typography
import javax.inject.Inject
class OptionFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: OptionViewModelFactory
private val vm by viewModels<OptionViewModel> { factory }
private val ctx get() = argString(CTX_KEY)
private val objectId get() = argString(OBJECT_ID_KEY)
private val relationKey get() = argString(RELATION_KEY)
private val optionId get() = argStringOrNull(OPTION_ID_KEY)
private val color get() = argStringOrNull(COLOR_KEY)
private val text get() = argStringOrNull(TEXT_KEY)
private val relationContext get() = requireArguments().getSerializable(RELATION_CONTEXT_KEY) as RelationContext
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(
typography = typography,
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(10.dp)),
colors = MaterialTheme.colors.copy(
surface = colorResource(id = R.color.context_menu_background)
)
) {
OptionWidget(
state = vm.viewState.collectAsStateWithLifecycle().value,
onButtonClicked = { vm.onButtonClick() },
onTextChanged = { vm.updateName(it) },
onColorChanged = { vm.updateColor(it) }
)
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
skipCollapsed()
expand()
}
override fun onStart() {
super.onStart()
jobs += lifecycleScope.subscribe(vm.command) { observeCommand(it) }
}
private fun observeCommand(command: OptionViewModel.Command) {
when (command) {
is OptionViewModel.Command.Dismiss -> dismiss()
}
}
override fun injectDependencies() {
val params = OptionViewModel.Params(
ctx = ctx,
relationKey = relationKey,
optionId = optionId,
color = color,
name = text,
objectId = objectId
)
inject(params)
}
override fun releaseDependencies() {
componentManager().optionObjectComponent.release()
}
private fun inject(params: OptionViewModel.Params) {
when (relationContext) {
RelationContext.OBJECT -> {
componentManager()
.optionObjectComponent.get(params)
.inject(this)
}
RelationContext.OBJECT_SET -> TODO()
RelationContext.DATA_VIEW -> TODO()
}
}
companion object {
const val CTX_KEY = "arg.option.ctx"
const val OBJECT_ID_KEY = "arg.option.object_id"
const val RELATION_KEY = "arg.option.relation_key"
const val OPTION_ID_KEY = "arg.option.option_id"
const val COLOR_KEY = "arg.option.color"
const val TEXT_KEY = "arg.option.text"
const val RELATION_CONTEXT_KEY = "arg.option.relation-context"
fun args(
ctx: String,
objectId: String,
relationKey: Key,
optionId: String?,
color: String?,
text: String?,
relationContext: RelationContext
) = bundleOf(
CTX_KEY to ctx,
OBJECT_ID_KEY to objectId,
RELATION_KEY to relationKey,
OPTION_ID_KEY to optionId,
COLOR_KEY to color,
TEXT_KEY to text,
RELATION_CONTEXT_KEY to relationContext
)
}
}

View File

@ -13,14 +13,18 @@ import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
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.subscribe
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.relations.value.tagstatus.Command
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.TagStatusViewModelFactory
@ -65,10 +69,33 @@ class TagStatusFragment : BaseBottomSheetComposeFragment() {
override fun onStart() {
super.onStart()
vm.onStart()
jobs += lifecycleScope.subscribe(vm.commands) { observeCommands(it) }
}
override fun onStop() {
super.onStop()
vm.onStop()
}
private fun observeCommands(command: Command) {
when (command) {
is Command.OpenOptionScreen -> {
val arg = OptionFragment.args(
ctx = command.ctx,
objectId = command.objectId,
relationKey = command.relationKey,
optionId = command.optionId,
color = command.color,
text = command.text,
relationContext = relationContext
)
findNavController().navigate(R.id.optionScreen, arg)
}
}
}
override fun injectDependencies() {
val params = TagStatusViewModel.Params(
val params = TagStatusViewModel.ViewModelParams(
ctx = ctx,
objectId = objectId,
relationKey = relationKey,

View File

@ -72,6 +72,7 @@
<include app:graph="@navigation/nav_editor_modal" />
<include app:graph="@navigation/nav_templates_modal" />
<include app:graph="@navigation/nav_relations" />
<navigation
android:id="@+id/dataViewNavigation"

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_relations"
app:startDestination="@id/tagStatusScreen">
<dialog
android:id="@+id/tagStatusScreen"
android:name="com.anytypeio.anytype.ui.relations.value.TagStatusFragment"
android:label="Tag status screen" />
<dialog
android:id="@+id/optionScreen"
android:name="com.anytypeio.anytype.ui.relations.value.OptionFragment"
android:label="Option create or edit screen" />
</navigation>

View File

@ -33,7 +33,7 @@ fun ItemMenu(
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 11.dp),
onClick = {
isMenuExpanded.value = false
action(TagStatusAction.Edit(item.optionId))
action(TagStatusAction.Edit(item))
},
) {
Text(

View File

@ -0,0 +1,296 @@
package com.anytypeio.anytype.core_ui.relations
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
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.extensions.dark
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.ButtonPrimary
import com.anytypeio.anytype.core_ui.views.ButtonSize
import com.anytypeio.anytype.core_ui.views.Caption1Regular
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.presentation.relations.option.OptionScreenViewState
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun OptionWidget(
state: OptionScreenViewState,
onButtonClicked: () -> Unit,
onTextChanged: (String) -> Unit,
onColorChanged: (ThemeColor) -> Unit
) {
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
contentAlignment = Alignment.TopCenter,
) {
val currentState by rememberUpdatedState(state)
val keyboardController = LocalSoftwareKeyboardController.current
OptionWidgetContent(
state = currentState,
onButtonClicked = onButtonClicked,
focusRequester = focusRequester,
keyboardController = keyboardController,
onTextChanged = onTextChanged,
onColorChanged = onColorChanged
)
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun OptionWidgetContent(
state: OptionScreenViewState,
onButtonClicked: () -> Unit,
focusRequester: FocusRequester,
keyboardController: SoftwareKeyboardController?,
onTextChanged: (String) -> Unit,
onColorChanged: (ThemeColor) -> Unit
) {
var selectedColor by remember { mutableStateOf(state.color) }
var editableText by remember { mutableStateOf(state.text) }
val (title, buttonText) = getTexts(state)
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState())
.padding(bottom = 20.dp, start = 20.dp, end = 20.dp)
) {
Header(text = title)
TextInput(
initialValue = state.text,
color = state.color,
onTextChanged = {
editableText = it
onTextChanged(it)
},
focusRequester = focusRequester,
keyboardController = keyboardController
)
Divider(paddingEnd = 0.dp, paddingStart = 0.dp)
Spacer(modifier = Modifier.height(26.dp))
Text(
text = stringResource(id = R.string.color),
style = Caption1Regular,
color = colorResource(id = R.color.text_secondary)
)
Spacer(modifier = Modifier.height(20.dp))
CirclesContainer(selectedColor = selectedColor) { newColor ->
selectedColor = newColor
onColorChanged(newColor)
}
Spacer(modifier = Modifier.height(115.dp))
ButtonPrimary(
text = buttonText,
modifier = Modifier
.fillMaxWidth()
.imePadding(),
onClick = { onButtonClicked() },
size = ButtonSize.Large
)
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CirclesContainer(selectedColor: ThemeColor, action: (ThemeColor) -> Unit) {
FlowRow(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
maxItemsInEachRow = 5
) {
val itemModifier = Modifier
.weight(1f)
.fillMaxWidth()
.clip(CircleShape)
.aspectRatio(1f)
.align(Alignment.CenterVertically)
ThemeColor.values().drop(1).forEach { color ->
Box(
itemModifier
.noRippleClickable { action(color) }
.background(
color = dark(color = color)
)
) {
if (selectedColor == color) {
Image(
modifier = Modifier.align(Alignment.Center),
painter = painterResource(id = R.drawable.ic_tick_24),
contentDescription = "option selected"
)
}
}
}
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun TextInput(
initialValue: String,
color: ThemeColor,
focusRequester: FocusRequester,
keyboardController: SoftwareKeyboardController?,
onTextChanged: (String) -> Unit
) {
var innerValue by remember { mutableStateOf(initialValue) }
val focusManager = LocalFocusManager.current
if (initialValue.isEmpty()) {
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
}
BasicTextField(
value = innerValue,
onValueChange = {
innerValue = it
onTextChanged(it)
},
textStyle = Title1.copy(color = dark(color = color)),
singleLine = true,
enabled = true,
cursorBrush = SolidColor(dark(color = color)),
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(start = 0.dp, top = 2.dp)
.focusRequester(focusRequester),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions {
keyboardController?.hide()
focusManager.clearFocus()
},
decorationBox = { innerTextField ->
if (innerValue.isEmpty()) {
Text(
text = stringResource(id = R.string.hint_enter_name),
style = Title1,
color = colorResource(id = R.color.text_tertiary),
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
)
}
innerTextField()
}
)
}
@Composable
private fun Header(text: String) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 6.dp)
) {
Dragger(modifier = Modifier.align(Alignment.Center))
}
// Main content box
Box(
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
) {
Text(
modifier = Modifier
.align(Alignment.Center)
.padding(horizontal = 74.dp),
text = text,
style = Title1.copy(),
color = colorResource(R.color.text_primary),
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
}
}
@Composable
private fun getTexts(state: OptionScreenViewState): Pair<String, String> {
return when (state) {
is OptionScreenViewState.Create -> {
stringResource(id = R.string.option_widget_create) to stringResource(id = R.string.create)
}
is OptionScreenViewState.Edit -> {
stringResource(id = R.string.option_widget_edit) to stringResource(id = R.string.apply)
}
}
}
@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF, device = Devices.PIXEL_4_XL)
@Composable
fun PreviewOptionWidget() {
OptionWidget(
state = OptionScreenViewState.Create(
text = "Urgent",
color = ThemeColor.BLUE,
),
onButtonClicked = {},
onTextChanged = {},
onColorChanged = {}
)
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18.633,6.403L11.131,18.191L5.47,12.53L6.53,11.47L10.869,15.809L17.367,5.597L18.633,6.403Z"
android:fillColor="@color/text_white"
android:fillType="evenOdd"/>
</vector>

View File

@ -4,4 +4,5 @@
<item>MIIEqDCCA5CgAwIBAgIJA071MA0GCSqGSIb3DQEBBAUAMIGUMQsww...</item>
</string-array>
<string name="msg_query_cannot_be_changed">This query can be changed on desktop only</string>
<string name="hint_enter_name">Enter name...</string>
</resources>

View File

@ -1251,4 +1251,7 @@
<string name="relation_value_create_new">Create \"%1$s\"</string>
<string name="option_widget_create">Create option</string>
<string name="option_widget_edit">Edit option</string>
</resources>

View File

@ -0,0 +1,189 @@
package com.anytypeio.anytype.presentation.relations.option
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_utils.ext.typeOf
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.relations.CreateRelationOption
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
class OptionViewModel(
private val vmParams: Params,
private val values: ObjectValueProvider,
private val createOption: CreateRelationOption,
private val setObjectDetails: SetObjectDetails,
private val dispatcher: Dispatcher<Payload>,
private val spaceManager: SpaceManager,
private val analytics: Analytics
) : BaseViewModel() {
val command = MutableSharedFlow<Command>(replay = 0)
val viewState: MutableStateFlow<OptionScreenViewState> =
if (vmParams.optionId == null) {
val color = if (vmParams.color != null) {
ThemeColor.fromCode(vmParams.color)
} else {
ThemeColor.values().drop(1).random()
}
MutableStateFlow(
OptionScreenViewState.Create(
text = vmParams.name.orEmpty(),
color = color
)
)
} else {
val color = if (vmParams.color != null) {
ThemeColor.fromCode(vmParams.color)
} else {
ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random()
}
MutableStateFlow(
OptionScreenViewState.Edit(
optionId = vmParams.optionId,
text = vmParams.name.orEmpty(),
color = color
)
)
}
fun updateName(name: String) {
viewState.value = when (val state = viewState.value) {
is OptionScreenViewState.Create -> state.copy(text = name)
is OptionScreenViewState.Edit -> state.copy(text = name)
}
}
fun updateColor(color: ThemeColor) {
viewState.value = when (val state = viewState.value) {
is OptionScreenViewState.Create -> state.copy(color = color)
is OptionScreenViewState.Edit -> state.copy(color = color)
}
}
fun onButtonClick() {
when (viewState.value) {
is OptionScreenViewState.Create -> createOption()
is OptionScreenViewState.Edit -> updateOption()
}
}
private fun createOption() {
viewModelScope.launch {
val params = CreateRelationOption.Params(
space = spaceManager.get(),
relation = vmParams.relationKey,
name = viewState.value.text,
color = viewState.value.color.code
)
if (params.name.isEmpty()) {
return@launch
}
createOption.invoke(params).proceed(
success = { option ->
proceedWithAddingTagToObject(
ctx = vmParams.ctx,
objectId = vmParams.objectId,
relationKey = vmParams.relationKey,
option = option
)
},
failure = { Timber.e(it, "Error while creating option") }
)
}
}
private fun updateOption() {
val optionId = vmParams.optionId ?: return
viewModelScope.launch {
val params = SetObjectDetails.Params(
ctx = optionId,
details = mapOf(
Relations.NAME to viewState.value.text,
Relations.RELATION_OPTION_COLOR to viewState.value.color.code
)
)
setObjectDetails.execute(params).fold(
onFailure = { Timber.e(it, "Error while updating option") },
onSuccess = {
dispatcher.send(it)
viewModelScope.sendAnalyticsRelationValueEvent(analytics)
command.emit(Command.Dismiss)
}
)
}
}
private suspend fun proceedWithAddingTagToObject(
ctx: Id,
objectId: Id,
relationKey: Key,
option: ObjectWrapper.Option
) {
Timber.d("Adding option to object with id: $objectId")
val obj = values.get(target = objectId, ctx = ctx)
val result = mutableListOf<Id>()
val value = obj[relationKey]
if (value is List<*>) {
result.addAll(value.typeOf())
} else if (value is Id) {
result.add(value)
}
result.add(option.id)
val params = SetObjectDetails.Params(
ctx = objectId,
details = mapOf(relationKey to result)
)
setObjectDetails.execute(params).fold(
onFailure = { Timber.e(it, "Error while adding tag to object") },
onSuccess = {
dispatcher.send(it)
viewModelScope.sendAnalyticsRelationValueEvent(analytics)
command.emit(Command.Dismiss)
}
)
}
data class Params(
val ctx: Id,
val relationKey: Key,
val objectId: Id,
val optionId: Id?,
val name: String?,
val color: String?
)
sealed class Command {
object Dismiss : Command()
}
}
sealed class OptionScreenViewState {
abstract val text: String
abstract val color: ThemeColor
data class Edit(
val optionId: Id,
override val text: String,
override val color: ThemeColor
) : OptionScreenViewState()
data class Create(
override val text: String,
override val color: ThemeColor
) : OptionScreenViewState()
}

View File

@ -0,0 +1,35 @@
package com.anytypeio.anytype.presentation.relations.option
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.relations.CreateRelationOption
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
import com.anytypeio.anytype.presentation.util.Dispatcher
import javax.inject.Inject
class OptionViewModelFactory @Inject constructor(
private val params: OptionViewModel.Params,
private val values: ObjectValueProvider,
private val createOption: CreateRelationOption,
private val setObjectDetails: SetObjectDetails,
private val dispatcher: Dispatcher<Payload>,
private val spaceManager: SpaceManager,
private val analytics: Analytics,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>
) = OptionViewModel(
vmParams = params,
values = values,
createOption = createOption,
setObjectDetails = setObjectDetails,
dispatcher = dispatcher,
spaceManager = spaceManager,
analytics = analytics,
) as T
}

View File

@ -3,25 +3,27 @@ package com.anytypeio.anytype.presentation.relations.value.tagstatus
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.ObjectWrapper
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.cancel
import com.anytypeio.anytype.core_utils.ext.typeOf
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.library.StoreSearchParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.options.GetOptions
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.sets.filterIdsById
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
@ -31,49 +33,64 @@ import kotlinx.coroutines.launch
import timber.log.Timber
class TagStatusViewModel(
private val params: Params,
private val viewModelParams: ViewModelParams,
private val relations: ObjectRelationProvider,
private val values: ObjectValueProvider,
private val storage: Editor.Storage,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val urlBuilder: UrlBuilder,
private val dispatcher: Dispatcher<Payload>,
private val setObjectDetails: UpdateDetail,
private val analytics: Analytics,
private val getOptions: GetOptions,
private val spaceManager: SpaceManager
private val spaceManager: SpaceManager,
private val subscription: StorelessSubscriptionContainer
) : BaseViewModel() {
val viewState = MutableStateFlow<TagStatusViewState>(TagStatusViewState.Loading)
private val query = MutableSharedFlow<String>()
private val query = MutableSharedFlow<String>(replay = 0)
private var isRelationNotEditable = false
val commands = MutableSharedFlow<Command>(replay = 0)
private val jobs = mutableListOf<Job>()
fun onStart() {
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 {
Timber.d("onStart, params: $viewModelParams")
jobs += viewModelScope.launch {
val relation = relations.get(relation = viewModelParams.relationKey)
val spaces = listOf(spaceManager.get())
val searchParams = StoreSearchParams(
subscription = SUB_MY_OPTIONS,
keys = ObjectSearchConstants.keysRelationOptions,
filters = ObjectSearchConstants.filterRelationOptions(
relationKey = viewModelParams.relationKey,
spaces = spaces
)
)
combine(
relations.observe(
relation = params.relationKey
),
values.subscribe(
ctx = params.ctx,
target = params.objectId
ctx = viewModelParams.ctx,
target = viewModelParams.objectId
),
query.onStart { emit("") }
) { relation, record, query ->
query.onStart { emit("") },
subscription.subscribe(searchParams)
) { record, query, options ->
setupIsRelationNotEditable(relation)
getAllOptions(
initViewState(
relation = relation,
record = record,
options = options
.map { ObjectWrapper.Option(map = it.map) }
.filter { it.name?.contains(query, true) == true },
query = query
)
}.collect()
}
}
fun onStop() {
viewModelScope.launch {
subscription.unsubscribe(listOf(SUB_MY_OPTIONS))
}
jobs.cancel()
}
fun onQueryChanged(input: String) {
viewModelScope.launch {
query.emit(input)
@ -96,10 +113,36 @@ class TagStatusViewModel(
viewState.value = currentState.copy(showItemMenu = action.item)
}
}
TagStatusAction.Plus -> TODO()
TagStatusAction.Plus -> emitCommand(
Command.OpenOptionScreen(
color = ThemeColor.values().drop(1).random().code,
relationKey = viewModelParams.relationKey,
ctx = viewModelParams.ctx,
objectId = viewModelParams.objectId
)
)
is TagStatusAction.Delete -> TODO()
is TagStatusAction.Duplicate -> TODO()
is TagStatusAction.Edit -> TODO()
is TagStatusAction.Edit -> {
val item = action.item
emitCommand(
Command.OpenOptionScreen(
optionId = item.optionId,
color = item.color.code,
text = item.name,
relationKey = viewModelParams.relationKey,
ctx = viewModelParams.ctx,
objectId = viewModelParams.objectId
)
)
}
}
}
private fun emitCommand(command: Command) {
viewModelScope.launch {
commands.emit(command)
}
}
@ -119,35 +162,27 @@ class TagStatusViewModel(
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
is RelationsListItem.CreateItem.Status -> {
emitCommand(
Command.OpenOptionScreen(
text = item.text,
relationKey = viewModelParams.relationKey,
ctx = viewModelParams.ctx,
objectId = viewModelParams.objectId
)
)
},
failure = {
Timber.e(it, "Error while getting options by id")
}
)
is RelationsListItem.CreateItem.Tag -> {
emitCommand(
Command.OpenOptionScreen(
text = item.text,
relationKey = viewModelParams.relationKey,
ctx = viewModelParams.ctx,
objectId = viewModelParams.objectId
)
)
}
}
}
private fun initViewState(
@ -159,7 +194,7 @@ class TagStatusViewModel(
val result = mutableListOf<RelationsListItem>()
when (relation.format) {
Relation.Format.STATUS -> {
val ids: List<Id> = when (val value = record[params.relationKey]) {
val ids: List<Id> = when (val value = record[viewModelParams.relationKey]) {
is Id -> listOf(value)
is List<*> -> value.typeOf()
else -> emptyList()
@ -172,7 +207,7 @@ class TagStatusViewModel(
)
}
Relation.Format.TAG -> {
val ids: List<Id> = when (val value = record[params.relationKey]) {
val ids: List<Id> = when (val value = record[viewModelParams.relationKey]) {
is Id -> listOf(value)
is List<*> -> value.typeOf()
else -> emptyList()
@ -208,9 +243,9 @@ class TagStatusViewModel(
private fun addTag(tag: Id) {
viewModelScope.launch {
val obj = values.get(ctx = params.ctx, target = params.objectId)
val obj = values.get(ctx = viewModelParams.ctx, target = viewModelParams.objectId)
val result = mutableListOf<Id>()
val value = obj[params.relationKey]
val value = obj[viewModelParams.relationKey]
if (value is List<*>) {
result.addAll(value.typeOf())
} else if (value is Id) {
@ -219,8 +254,8 @@ class TagStatusViewModel(
result.add(tag)
setObjectDetails(
UpdateDetail.Params(
target = params.objectId,
key = params.relationKey,
target = viewModelParams.objectId,
key = viewModelParams.relationKey,
value = result
)
).process(
@ -235,12 +270,12 @@ class TagStatusViewModel(
private fun removeTag(tag: Id) {
viewModelScope.launch {
val obj = values.get(ctx = params.ctx, target = params.objectId)
val remaining = obj[params.relationKey].filterIdsById(tag)
val obj = values.get(ctx = viewModelParams.ctx, target = viewModelParams.objectId)
val remaining = obj[viewModelParams.relationKey].filterIdsById(tag)
setObjectDetails(
UpdateDetail.Params(
target = params.objectId,
key = params.relationKey,
target = viewModelParams.objectId,
key = viewModelParams.relationKey,
value = remaining
)
).process(
@ -256,8 +291,8 @@ class TagStatusViewModel(
viewModelScope.launch {
setObjectDetails(
UpdateDetail.Params(
target = params.objectId,
key = params.relationKey,
target = viewModelParams.objectId,
key = viewModelParams.relationKey,
value = listOf(status)
)
).process(
@ -274,8 +309,8 @@ class TagStatusViewModel(
viewModelScope.launch {
setObjectDetails(
UpdateDetail.Params(
target = params.objectId,
key = params.relationKey,
target = viewModelParams.objectId,
key = viewModelParams.relationKey,
value = null
)
).process(
@ -322,7 +357,7 @@ class TagStatusViewModel(
}
private fun setupIsRelationNotEditable(relation: ObjectWrapper.Relation) {
isRelationNotEditable = params.isLocked
isRelationNotEditable = viewModelParams.isLocked
|| storage.objectRestrictions.current().contains(ObjectRestriction.RELATIONS)
|| relation.isReadonlyValue
|| relation.isHidden == true
@ -331,15 +366,33 @@ class TagStatusViewModel(
|| !relation.isValid
}
data class Params(
override fun onCleared() {
super.onCleared()
viewModelScope.launch {
subscription.unsubscribe(listOf(SUB_MY_OPTIONS))
}
}
data class ViewModelParams(
val ctx: Id,
val objectId: Id,
val relationKey: Id,
val relationKey: Key,
val isLocked: Boolean,
val relationContext: RelationContext
)
}
sealed class Command {
data class OpenOptionScreen(
val ctx: Id,
val objectId: Id,
val relationKey: Key,
val optionId: Id? = null,
val color: String? = null,
val text: String? = null,
) : Command()
}
sealed class TagStatusViewState {
object Loading : TagStatusViewState()
@ -359,32 +412,36 @@ sealed class TagStatusAction {
data class LongClick(val item: RelationsListItem.Item) : TagStatusAction()
object Clear : TagStatusAction()
object Plus : TagStatusAction()
data class Edit(val optionId: Id) : TagStatusAction()
data class Edit(val item: RelationsListItem.Item) : TagStatusAction()
data class Delete(val optionId: Id) : TagStatusAction()
data class Duplicate(val optionId: Id) : TagStatusAction()
}
enum class RelationContext{ OBJECT, OBJECT_SET, DATA_VIEW }
enum class RelationContext { OBJECT, OBJECT_SET, DATA_VIEW }
sealed class RelationsListItem {
sealed class Item : RelationsListItem() {
abstract val optionId: Id
abstract val name: String
abstract val color: ThemeColor
abstract val isSelected: Boolean
data class Tag(
override val optionId: Id,
val name: String,
val color: ThemeColor,
val isSelected: Boolean,
override val name: String,
override val color: ThemeColor,
override 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
override val name: String,
override val color: ThemeColor,
override val isSelected: Boolean
) : Item()
}
@ -394,4 +451,6 @@ sealed class RelationsListItem {
class Tag(text: String) : CreateItem(text)
class Status(text: String) : CreateItem(text)
}
}
}
const val SUB_MY_OPTIONS = "subscription.relation_options"

View File

@ -4,10 +4,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.options.GetOptions
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
@ -16,32 +14,28 @@ import com.anytypeio.anytype.presentation.util.Dispatcher
import javax.inject.Inject
class TagStatusViewModelFactory @Inject constructor(
private val params: TagStatusViewModel.Params,
private val params: TagStatusViewModel.ViewModelParams,
private val relations: ObjectRelationProvider,
private val values: ObjectValueProvider,
private val storage: Editor.Storage,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val urlBuilder: UrlBuilder,
private val dispatcher: Dispatcher<Payload>,
private val setObjectDetails: UpdateDetail,
private val analytics: Analytics,
private val getOptions: GetOptions,
private val spaceManager: SpaceManager
private val spaceManager: SpaceManager,
private val subscription: StorelessSubscriptionContainer
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>
) = TagStatusViewModel(
params = params,
viewModelParams = params,
relations = relations,
values = values,
storage = storage,
storeOfObjectTypes = storeOfObjectTypes,
dispatcher = dispatcher,
setObjectDetails = setObjectDetails,
urlBuilder = urlBuilder,
analytics = analytics,
getOptions = getOptions,
spaceManager = spaceManager,
subscription = subscription
) as T
}

View File

@ -939,4 +939,45 @@ object ObjectSearchConstants {
value = types
)
)
fun filterRelationOptions(relationKey: Key, spaces: List<Id>) : List<DVFilter> = listOf(
DVFilter(
relation = Relations.RELATION_KEY,
condition = DVFilterCondition.EQUAL,
value = relationKey
),
DVFilter(
relation = Relations.LAYOUT,
condition = DVFilterCondition.EQUAL,
value = ObjectType.Layout.RELATION_OPTION.code.toDouble()
),
DVFilter(
relation = Relations.IS_ARCHIVED,
condition = DVFilterCondition.NOT_EQUAL,
value = true
),
DVFilter(
relation = Relations.IS_DELETED,
condition = DVFilterCondition.NOT_EQUAL,
value = true
),
DVFilter(
relation = Relations.IS_HIDDEN,
condition = DVFilterCondition.NOT_EQUAL,
value = true
),
DVFilter(
relation = Relations.SPACE_ID,
condition = DVFilterCondition.IN,
value = spaces
)
)
val keysRelationOptions = listOf(
Relations.ID,
Relations.SPACE_ID,
Relations.NAME,
Relations.RELATION_OPTION_COLOR,
Relations.RELATION_KEY
)
}