mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 13:57:10 +09:00
DROID-1029 Collection | Enhancement | BackLink or add to Collection (#3121)
* DROID-1029 back link di * DROID-1029 back link, fragment + viewmodel * DROID-1029 filters * DROID-1029 integration * DROID-1029 back link screen + vm * DROID-1029 object menu * DROID-1029 open target * DROID-1029 snackbar fixed * DROID-1029 else * DROID-1029 pr fix
This commit is contained in:
parent
028049fe50
commit
1daec1007d
24 changed files with 718 additions and 39 deletions
|
@ -10,6 +10,7 @@ import com.anytypeio.anytype.di.feature.CreateAccountModule
|
|||
import com.anytypeio.anytype.di.feature.CreateBookmarkModule
|
||||
import com.anytypeio.anytype.di.feature.CreateDataViewViewerModule
|
||||
import com.anytypeio.anytype.di.feature.CreateObjectModule
|
||||
import com.anytypeio.anytype.di.feature.DaggerBacklinkOrAddToObjectComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerSplashComponent
|
||||
import com.anytypeio.anytype.di.feature.DebugSettingsModule
|
||||
import com.anytypeio.anytype.di.feature.EditDataViewViewerModule
|
||||
|
@ -825,6 +826,13 @@ class ComponentManager(
|
|||
.build()
|
||||
}
|
||||
|
||||
val backLinkOrAddToObjectComponent = ComponentWithParams { ctx: Id ->
|
||||
DaggerBacklinkOrAddToObjectComponent.builder()
|
||||
.withContext(ctx)
|
||||
.withDependencies(findComponentDependencies())
|
||||
.build()
|
||||
}
|
||||
|
||||
val typeCreationComponent = Component {
|
||||
DaggerTypeCreationComponent
|
||||
.factory()
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.interactor.sets.GetObjectTypes
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.linking.BackLinkOrAddToObjectViewModelFactory
|
||||
import com.anytypeio.anytype.ui.linking.BacklinkOrAddToObjectFragment
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
|
||||
@PerScreen
|
||||
@Component(
|
||||
dependencies = [BacklinkOrAddToObjectDependencies::class],
|
||||
modules = [
|
||||
BackLinkToObjectModule::class,
|
||||
BackLinkToObjectModule.Declarations::class
|
||||
]
|
||||
)
|
||||
interface BacklinkOrAddToObjectComponent {
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
fun withDependencies(dependency: BacklinkOrAddToObjectDependencies): Builder
|
||||
|
||||
@BindsInstance
|
||||
fun withContext(context: Id): Builder
|
||||
|
||||
fun build(): BacklinkOrAddToObjectComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: BacklinkOrAddToObjectFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
object BackLinkToObjectModule {
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun searchObjects(repo: BlockRepository): SearchObjects = SearchObjects(repo = repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideGetObjectTypesUseCase(
|
||||
repository: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): GetObjectTypes = GetObjectTypes(repository, dispatchers)
|
||||
|
||||
@Module
|
||||
interface Declarations {
|
||||
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindViewModelFactory(factory: BackLinkOrAddToObjectViewModelFactory): ViewModelProvider.Factory
|
||||
}
|
||||
}
|
||||
|
||||
interface BacklinkOrAddToObjectDependencies : ComponentDependencies {
|
||||
fun authRepository(): AuthRepository
|
||||
fun blockRepository(): BlockRepository
|
||||
fun workspaceManager(): WorkspaceManager
|
||||
fun urlBuilder(): UrlBuilder
|
||||
fun dispatchers(): AppCoroutineDispatchers
|
||||
fun analytics(): Analytics
|
||||
}
|
|
@ -10,6 +10,7 @@ import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
|||
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
|
||||
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.AddToFavorite
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.RemoveFromFavorite
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -110,7 +111,8 @@ object ObjectMenuModule {
|
|||
dispatcher: Dispatcher<Payload>,
|
||||
updateFields: UpdateFields,
|
||||
featureToggles: FeatureToggles,
|
||||
delegator: Delegator<Action>
|
||||
delegator: Delegator<Action>,
|
||||
addObjectToCollection: AddObjectToCollection
|
||||
): ObjectMenuViewModel.Factory = ObjectMenuViewModel.Factory(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
duplicateObject = duplicateObject,
|
||||
|
@ -124,7 +126,8 @@ object ObjectMenuModule {
|
|||
dispatcher = dispatcher,
|
||||
updateFields = updateFields,
|
||||
delegator = delegator,
|
||||
menuOptionsProvider = createMenuOptionsProvider(storage, featureToggles)
|
||||
menuOptionsProvider = createMenuOptionsProvider(storage, featureToggles),
|
||||
addObjectToCollection = addObjectToCollection
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
@ -134,6 +137,17 @@ object ObjectMenuModule {
|
|||
restrictions = storage.objectRestrictions.stream(),
|
||||
featureToggles = featureToggles
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun provideAddObjectToCollection(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): AddObjectToCollection = AddObjectToCollection(
|
||||
repo = repo,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
}
|
||||
|
||||
@Module
|
||||
|
@ -153,7 +167,8 @@ object ObjectSetMenuModule {
|
|||
analytics: Analytics,
|
||||
state: MutableStateFlow<ObjectState>,
|
||||
featureToggles: FeatureToggles,
|
||||
dispatcher: Dispatcher<Payload>
|
||||
dispatcher: Dispatcher<Payload>,
|
||||
addObjectToCollection: AddObjectToCollection
|
||||
): ObjectSetMenuViewModel.Factory = ObjectSetMenuViewModel.Factory(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
addToFavorite = addToFavorite,
|
||||
|
@ -165,7 +180,8 @@ object ObjectSetMenuModule {
|
|||
analytics = analytics,
|
||||
objectState = state,
|
||||
dispatcher = dispatcher,
|
||||
menuOptionsProvider = createMenuOptionsProvider(state, featureToggles)
|
||||
menuOptionsProvider = createMenuOptionsProvider(state, featureToggles),
|
||||
addObjectToCollection = addObjectToCollection
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.anytypeio.anytype.app.AndroidApplication
|
|||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.di.common.ComponentDependenciesKey
|
||||
import com.anytypeio.anytype.di.feature.AuthSubComponent
|
||||
import com.anytypeio.anytype.di.feature.BacklinkOrAddToObjectDependencies
|
||||
import com.anytypeio.anytype.di.feature.CreateBookmarkSubComponent
|
||||
import com.anytypeio.anytype.di.feature.CreateObjectSubComponent
|
||||
import com.anytypeio.anytype.di.feature.DebugSettingsSubComponent
|
||||
|
@ -72,7 +73,8 @@ interface MainComponent :
|
|||
RelationEditDependencies,
|
||||
SplashDependencies,
|
||||
DeletedAccountDependencies,
|
||||
MigrationErrorDependencies {
|
||||
MigrationErrorDependencies,
|
||||
BacklinkOrAddToObjectDependencies {
|
||||
|
||||
fun inject(app: AndroidApplication)
|
||||
|
||||
|
@ -170,4 +172,9 @@ private abstract class ComponentDependenciesModule private constructor() {
|
|||
@ComponentDependenciesKey(MigrationErrorDependencies::class)
|
||||
abstract fun migrationErrorDependencies(component: MainComponent) : ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(BacklinkOrAddToObjectDependencies::class)
|
||||
abstract fun provideBackLinkDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
}
|
|
@ -34,7 +34,7 @@ class NavigationRouter(
|
|||
command.id,
|
||||
command.editorSettings
|
||||
)
|
||||
is AppNavigation.Command.OpenObjectSet -> navigation.openObjectSet(
|
||||
is AppNavigation.Command.OpenSetOrCollection -> navigation.openObjectSet(
|
||||
command.target,
|
||||
command.isPopUpToDashboard
|
||||
)
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.navigation.fragment.findNavController
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_ui.features.objects.ObjectActionAdapter
|
||||
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.anytypeio.anytype.core_ui.reactive.click
|
||||
|
@ -27,13 +28,15 @@ import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectFragment
|
|||
import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectSetFragment
|
||||
import com.anytypeio.anytype.ui.editor.layout.ObjectLayoutFragment
|
||||
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase
|
||||
import com.anytypeio.anytype.ui.moving.MoveToFragment
|
||||
import com.anytypeio.anytype.ui.linking.BacklinkAction
|
||||
import com.anytypeio.anytype.ui.linking.BacklinkOrAddToObjectFragment
|
||||
import com.anytypeio.anytype.ui.moving.OnMoveToAction
|
||||
import com.anytypeio.anytype.ui.relations.ObjectRelationListFragment
|
||||
|
||||
abstract class ObjectMenuBaseFragment :
|
||||
BaseBottomSheetFragment<FragmentObjectMenuBinding>(),
|
||||
OnMoveToAction {
|
||||
OnMoveToAction,
|
||||
BacklinkAction {
|
||||
|
||||
protected val ctx get() = arg<Id>(CTX_KEY)
|
||||
private val isProfile get() = arg<Boolean>(IS_PROFILE_KEY)
|
||||
|
@ -193,13 +196,7 @@ abstract class ObjectMenuBaseFragment :
|
|||
|
||||
|
||||
private fun openLinkChooser() {
|
||||
val fr = MoveToFragment.new(
|
||||
ctx = ctx,
|
||||
blocks = emptyList(),
|
||||
restorePosition = null,
|
||||
restoreBlock = null,
|
||||
title = getString(R.string.link_to)
|
||||
)
|
||||
val fr = BacklinkOrAddToObjectFragment.new(ctx = ctx)
|
||||
fr.showChildFragment()
|
||||
}
|
||||
|
||||
|
@ -208,13 +205,17 @@ abstract class ObjectMenuBaseFragment :
|
|||
dialog?.window
|
||||
?.decorView
|
||||
?.showActionableSnackBar(
|
||||
command.currentObjectName,
|
||||
command.targetObjectName,
|
||||
command.icon,
|
||||
R.string.snack_link_to,
|
||||
binding.anchor
|
||||
from = command.currentObjectName,
|
||||
to = command.targetObjectName,
|
||||
icon = command.icon,
|
||||
middleString = R.string.snack_link_to,
|
||||
anchor = binding.anchor
|
||||
) {
|
||||
vm.proceedWithOpeningPage(command.id)
|
||||
if (command.isCollection) {
|
||||
vm.proceedWithOpeningCollection(command.id)
|
||||
} else {
|
||||
vm.proceedWithOpeningPage(command.id)
|
||||
}
|
||||
}
|
||||
}, 300L)
|
||||
}
|
||||
|
@ -230,6 +231,17 @@ abstract class ObjectMenuBaseFragment :
|
|||
vm.onLinkedMyselfTo(myself = ctx, addTo = target, fromName)
|
||||
}
|
||||
|
||||
override fun backLink(id: Id, name: String, layout: ObjectType.Layout?, icon: ObjectIcon) {
|
||||
vm.onBackLinkOrAddToObjectAction(
|
||||
ctx = ctx,
|
||||
backLinkId = id,
|
||||
backLinkName = name,
|
||||
backLinkLayout = layout,
|
||||
backLinkIcon = icon,
|
||||
fromName = fromName.orEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onMoveToClose(blocks: List<Id>, restorePosition: Int?, restoreBlock: Id?) {}
|
||||
|
||||
override fun inflateBinding(
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
package com.anytypeio.anytype.ui.linking
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.drawable
|
||||
import com.anytypeio.anytype.core_utils.ext.hideSoftInput
|
||||
import com.anytypeio.anytype.core_utils.ext.invisible
|
||||
import com.anytypeio.anytype.core_utils.ext.statusBarHeight
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.visible
|
||||
import com.anytypeio.anytype.core_utils.ext.withParent
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetTextInputFragment
|
||||
import com.anytypeio.anytype.databinding.FragmentObjectSearchBinding
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.presentation.linking.BackLinkOrAddToObjectViewModel
|
||||
import com.anytypeio.anytype.presentation.linking.BackLinkOrAddToObjectViewModelFactory
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchView
|
||||
import com.anytypeio.anytype.ui.moving.hideProgress
|
||||
import com.anytypeio.anytype.ui.moving.showProgress
|
||||
import com.anytypeio.anytype.ui.search.ObjectSearchFragment
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class BacklinkOrAddToObjectFragment :
|
||||
BaseBottomSheetTextInputFragment<FragmentObjectSearchBinding>() {
|
||||
private val vm by viewModels<BackLinkOrAddToObjectViewModel> { factory }
|
||||
|
||||
@Inject
|
||||
lateinit var factory: BackLinkOrAddToObjectViewModelFactory
|
||||
|
||||
private val ctx get() = arg<Id>(ARG_CTX)
|
||||
private lateinit var clearSearchText: View
|
||||
private lateinit var filterInputField: EditText
|
||||
|
||||
override val textInput: EditText get() = binding.searchView.root.findViewById(R.id.filterInputField)
|
||||
|
||||
private val moveToAdapter by lazy {
|
||||
DefaultObjectViewAdapter(
|
||||
onDefaultObjectClicked = vm::onObjectClicked
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupFullHeight()
|
||||
setTransparent()
|
||||
BottomSheetBehavior.from(binding.sheet).apply {
|
||||
state = BottomSheetBehavior.STATE_EXPANDED
|
||||
isHideable = true
|
||||
skipCollapsed = true
|
||||
addBottomSheetCallback(
|
||||
object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
||||
vm.onDialogCancelled()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
vm.state.observe(viewLifecycleOwner) { observe(it) }
|
||||
clearSearchText = binding.searchView.root.findViewById(R.id.clearSearchText)
|
||||
filterInputField = binding.searchView.root.findViewById(R.id.filterInputField)
|
||||
filterInputField.setHint(R.string.search)
|
||||
filterInputField.imeOptions = EditorInfo.IME_ACTION_DONE
|
||||
filterInputField.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
return@setOnEditorActionListener false
|
||||
}
|
||||
true
|
||||
}
|
||||
initialize()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
lifecycleScope.launch {
|
||||
jobs += subscribe(vm.commands) { execute(it) }
|
||||
}
|
||||
super.onStart()
|
||||
vm.onStart(EventsDictionary.Routes.screenSettings, ignore = ctx)
|
||||
expand()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
vm.onStop()
|
||||
}
|
||||
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
super.onCancel(dialog)
|
||||
vm.onDialogCancelled()
|
||||
}
|
||||
|
||||
private fun observe(state: ObjectSearchView) {
|
||||
when (state) {
|
||||
ObjectSearchView.Loading -> {
|
||||
with(binding) {
|
||||
recyclerView.invisible()
|
||||
tvScreenStateMessage.invisible()
|
||||
tvScreenStateSubMessage.invisible()
|
||||
showProgress()
|
||||
}
|
||||
}
|
||||
is ObjectSearchView.Success -> {
|
||||
with(binding) {
|
||||
hideProgress()
|
||||
tvScreenStateMessage.invisible()
|
||||
tvScreenStateSubMessage.invisible()
|
||||
recyclerView.visible()
|
||||
moveToAdapter.submitList(state.objects)
|
||||
}
|
||||
}
|
||||
ObjectSearchView.EmptyPages -> {
|
||||
with(binding) {
|
||||
hideProgress()
|
||||
recyclerView.invisible()
|
||||
tvScreenStateMessage.visible()
|
||||
tvScreenStateMessage.text = getString(R.string.search_empty_pages)
|
||||
tvScreenStateSubMessage.invisible()
|
||||
}
|
||||
}
|
||||
is ObjectSearchView.NoResults -> {
|
||||
with(binding) {
|
||||
hideProgress()
|
||||
recyclerView.invisible()
|
||||
tvScreenStateMessage.visible()
|
||||
tvScreenStateMessage.text =
|
||||
getString(R.string.search_no_results, state.searchText)
|
||||
tvScreenStateSubMessage.visible()
|
||||
}
|
||||
}
|
||||
is ObjectSearchView.Error -> {
|
||||
with(binding) {
|
||||
hideProgress()
|
||||
recyclerView.invisible()
|
||||
tvScreenStateMessage.visible()
|
||||
tvScreenStateMessage.text = state.error
|
||||
tvScreenStateSubMessage.invisible()
|
||||
}
|
||||
}
|
||||
else -> Timber.d("Skipping state: $state")
|
||||
}
|
||||
}
|
||||
|
||||
private fun execute(command: BackLinkOrAddToObjectViewModel.Command) {
|
||||
hideSoftInput()
|
||||
when (command) {
|
||||
BackLinkOrAddToObjectViewModel.Command.Exit -> {
|
||||
dismiss()
|
||||
}
|
||||
is BackLinkOrAddToObjectViewModel.Command.CreateBacklink -> {
|
||||
withParent<BacklinkAction> {
|
||||
backLink(
|
||||
id = command.id,
|
||||
name = command.name,
|
||||
layout = command.layout,
|
||||
icon = command.icon
|
||||
)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initialize() {
|
||||
with(binding.tvScreenTitle) {
|
||||
text = getString(R.string.link_to)
|
||||
visible()
|
||||
}
|
||||
binding.recyclerView.invisible()
|
||||
binding.tvScreenStateMessage.invisible()
|
||||
binding.hideProgress()
|
||||
clearSearchText.setOnClickListener {
|
||||
filterInputField.setText(ObjectSearchFragment.EMPTY_FILTER_TEXT)
|
||||
clearSearchText.invisible()
|
||||
}
|
||||
filterInputField.doAfterTextChanged { newText ->
|
||||
if (newText != null) {
|
||||
vm.onSearchTextChanged(newText.toString())
|
||||
}
|
||||
if (newText.isNullOrEmpty()) {
|
||||
clearSearchText.invisible()
|
||||
} else {
|
||||
clearSearchText.visible()
|
||||
}
|
||||
}
|
||||
with(binding.recyclerView) {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = moveToAdapter
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
|
||||
setDrawable(drawable(R.drawable.divider_object_search))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFullHeight() {
|
||||
val lp = (binding.root.layoutParams as FrameLayout.LayoutParams)
|
||||
lp.height =
|
||||
Resources.getSystem().displayMetrics.heightPixels - requireActivity().statusBarHeight
|
||||
binding.root.layoutParams = lp
|
||||
}
|
||||
|
||||
private fun setTransparent() {
|
||||
with(binding.root) {
|
||||
background = null
|
||||
(parent as? View)?.setBackgroundColor(Color.TRANSPARENT)
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager().backLinkOrAddToObjectComponent.get(ctx).inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().backLinkOrAddToObjectComponent.release()
|
||||
}
|
||||
|
||||
override fun inflateBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
): FragmentObjectSearchBinding = FragmentObjectSearchBinding.inflate(
|
||||
inflater, container, false
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val ARG_CTX = "arg.bind_link.ctx"
|
||||
|
||||
fun new(ctx: Id) = BacklinkOrAddToObjectFragment().apply {
|
||||
arguments = bundleOf(ARG_CTX to ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BacklinkAction {
|
||||
fun backLink(id: Id, name: String, layout: ObjectType.Layout?, icon: ObjectIcon)
|
||||
}
|
|
@ -192,7 +192,7 @@ abstract class RelationValueBaseFragment<T: ViewBinding> : BaseBottomSheetFragme
|
|||
|
||||
private fun navigate(command: AppNavigation.Command) {
|
||||
when (command) {
|
||||
is AppNavigation.Command.OpenObjectSet -> {
|
||||
is AppNavigation.Command.OpenSetOrCollection -> {
|
||||
findNavController().navigate(
|
||||
R.id.dataViewNavigation,
|
||||
bundleOf(ObjectSetFragment.CONTEXT_ID_KEY to command.target)
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
android:top="48dp">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/background_secondary" />
|
||||
<corners android:radius="12dp" />
|
||||
<solid android:color="@color/snackbar_background" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -9,7 +9,8 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/snackbar_text"
|
||||
style="@style/TextView.UXStyle.Captions.1.Regular"
|
||||
style="@style/TextView.UXStyle.Captions.1.Medium"
|
||||
android:textColor="@color/text_button_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="77dp"
|
||||
|
@ -43,7 +44,8 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/snackbar_action"
|
||||
style="@style/TextView.UXStyle.Captions.1.Regular"
|
||||
style="@style/TextView.UXStyle.Captions.1.Medium"
|
||||
android:textColor="@color/text_button_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
|
|
|
@ -22,6 +22,7 @@ object ObjectTypeIds {
|
|||
const val DASHBOARD = "ot-dashboard"
|
||||
const val BOOKMARK = "ot-bookmark"
|
||||
const val RELATION_OPTION = "ot-relationOption"
|
||||
const val SPACE = "ot-space"
|
||||
|
||||
const val DEFAULT_OBJECT_TYPE_PREFIX = "ot-"
|
||||
}
|
||||
|
|
|
@ -90,5 +90,6 @@
|
|||
<color name="colorPrimary">#FF5722</color>
|
||||
<color name="colorPrimaryDark">#000000</color>
|
||||
<color name="colorAccent">#3F51B5</color>
|
||||
<color name="snackbar_background">#FFFFFF</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -211,5 +211,6 @@
|
|||
<color name="colorPrimary">#FF5722</color>
|
||||
<color name="colorPrimaryDark">#000000</color>
|
||||
<color name="colorAccent">#3F51B5</color>
|
||||
<color name="snackbar_background">#000000</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -22,5 +22,6 @@ sealed class Action {
|
|||
object SearchOnPage: Action()
|
||||
object UndoRedo : Action()
|
||||
data class OpenObject(val id: Id) : Action()
|
||||
data class OpenCollection(val id: Id) : Action()
|
||||
data class Duplicate(val id: Id) : Action()
|
||||
}
|
|
@ -386,6 +386,7 @@ class EditorViewModel(
|
|||
Action.SearchOnPage -> onEnterSearchModeClicked()
|
||||
Action.UndoRedo -> onUndoRedoActionClicked()
|
||||
is Action.OpenObject -> proceedWithOpeningObject(action.id)
|
||||
is Action.OpenCollection -> proceedWithOpeningDataViewObject(action.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4143,7 +4144,7 @@ class EditorViewModel(
|
|||
Timber.e(it, "Error while closing object")
|
||||
navigate(
|
||||
EventWrapper(
|
||||
AppNavigation.Command.OpenObjectSet(
|
||||
AppNavigation.Command.OpenSetOrCollection(
|
||||
target,
|
||||
isPopUpToDashboard
|
||||
)
|
||||
|
@ -4153,7 +4154,7 @@ class EditorViewModel(
|
|||
onSuccess = {
|
||||
navigate(
|
||||
EventWrapper(
|
||||
AppNavigation.Command.OpenObjectSet(
|
||||
AppNavigation.Command.OpenSetOrCollection(
|
||||
target,
|
||||
isPopUpToDashboard
|
||||
)
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package com.anytypeio.anytype.presentation.linking
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
import com.anytypeio.anytype.domain.block.interactor.sets.GetObjectTypes
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchViewModel
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class BackLinkOrAddToObjectViewModel(
|
||||
urlBuilder: UrlBuilder,
|
||||
searchObjects: SearchObjects,
|
||||
getObjectTypes: GetObjectTypes,
|
||||
private val analytics: Analytics,
|
||||
private val workspaceManager: WorkspaceManager
|
||||
) : ObjectSearchViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getObjectTypes = getObjectTypes,
|
||||
searchObjects = searchObjects,
|
||||
analytics = analytics,
|
||||
workspaceManager = workspaceManager
|
||||
) {
|
||||
|
||||
private val _commands = MutableSharedFlow<Command>(replay = 0)
|
||||
val commands: SharedFlow<Command> = _commands
|
||||
|
||||
/**
|
||||
* Adding a source object as a backlink is only possible in objects
|
||||
* with these Layouts and also in Collection objects.
|
||||
*/
|
||||
private val supported = listOf(
|
||||
ObjectType.Layout.BASIC,
|
||||
ObjectType.Layout.PROFILE,
|
||||
ObjectType.Layout.TODO,
|
||||
ObjectType.Layout.NOTE,
|
||||
ObjectType.Layout.COLLECTION
|
||||
)
|
||||
|
||||
override suspend fun getSearchObjectsParams(ignore: Id?) = SearchObjects.Params(
|
||||
limit = SEARCH_LIMIT,
|
||||
filters = ObjectSearchConstants.filtersBackLinkOrAddToObject(
|
||||
ignore = ignore,
|
||||
workspaceId = workspaceManager.getCurrentWorkspace()
|
||||
),
|
||||
sorts = ObjectSearchConstants.sortBackLinkOrAddToObject,
|
||||
fulltext = EMPTY_QUERY,
|
||||
keys = ObjectSearchConstants.defaultKeys
|
||||
)
|
||||
|
||||
override fun onObjectClicked(view: DefaultObjectView) {
|
||||
sendSearchResultEvent(view.id)
|
||||
viewModelScope.launch {
|
||||
_commands.emit(
|
||||
Command.CreateBacklink(
|
||||
id = view.id,
|
||||
name = view.name,
|
||||
layout = view.layout,
|
||||
icon = view.icon
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogCancelled() {
|
||||
viewModelScope.launch {
|
||||
_commands.emit(Command.Exit)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setObjects(data: List<ObjectWrapper.Basic>) {
|
||||
objects.emit(
|
||||
Resultat.success(data.filter {
|
||||
supported.contains(it.layout)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
sealed class Command {
|
||||
object Exit : Command()
|
||||
data class CreateBacklink(
|
||||
val id: Id,
|
||||
val name: String,
|
||||
val layout: ObjectType.Layout?,
|
||||
val icon: ObjectIcon
|
||||
) : Command()
|
||||
}
|
||||
}
|
|
@ -5,11 +5,14 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_utils.tools.UrlValidator
|
||||
import com.anytypeio.anytype.domain.block.interactor.sets.GetObjectTypes
|
||||
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.page.AddBackLinkToObject
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.editor.Editor
|
||||
import javax.inject.Inject
|
||||
|
||||
class LinkToObjectViewModelFactory(
|
||||
private val urlBuilder: UrlBuilder,
|
||||
|
@ -53,4 +56,24 @@ class LinkToObjectOrWebViewModelFactory(
|
|||
workspaceManager = workspaceManager
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
||||
class BackLinkOrAddToObjectViewModelFactory @Inject constructor(
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val getObjectTypes: GetObjectTypes,
|
||||
private val searchObjects: SearchObjects,
|
||||
private val workspaceManager: WorkspaceManager,
|
||||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return BackLinkOrAddToObjectViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getObjectTypes = getObjectTypes,
|
||||
searchObjects = searchObjects,
|
||||
analytics = analytics,
|
||||
workspaceManager = workspaceManager
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -105,7 +105,7 @@ interface AppNavigation {
|
|||
data class ExitToDesktopAndOpenPage(val pageId: String) : Command()
|
||||
object OpenPageSearch : Command()
|
||||
|
||||
data class OpenObjectSet(val target: String, val isPopUpToDashboard: Boolean = false) :
|
||||
data class OpenSetOrCollection(val target: String, val isPopUpToDashboard: Boolean = false) :
|
||||
Command()
|
||||
|
||||
data class LaunchObjectSet(val target: Id) : Command()
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.anytypeio.anytype.core_models.Payload
|
|||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
|
||||
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.AddToFavorite
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.RemoveFromFavorite
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -41,7 +42,8 @@ class ObjectMenuViewModel(
|
|||
private val debugTreeShareDownloader: DebugTreeShareDownloader,
|
||||
private val storage: Editor.Storage,
|
||||
private val analytics: Analytics,
|
||||
private val updateFields: UpdateFields
|
||||
private val updateFields: UpdateFields,
|
||||
private val addObjectToCollection: AddObjectToCollection
|
||||
) : ObjectMenuViewModelBase(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
addToFavorite = addToFavorite,
|
||||
|
@ -52,7 +54,8 @@ class ObjectMenuViewModel(
|
|||
urlBuilder = urlBuilder,
|
||||
dispatcher = dispatcher,
|
||||
analytics = analytics,
|
||||
menuOptionsProvider = menuOptionsProvider
|
||||
menuOptionsProvider = menuOptionsProvider,
|
||||
addObjectToCollection = addObjectToCollection
|
||||
) {
|
||||
|
||||
private val objectRestrictions = storage.objectRestrictions.current()
|
||||
|
@ -294,7 +297,8 @@ class ObjectMenuViewModel(
|
|||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val updateFields: UpdateFields,
|
||||
private val delegator: Delegator<Action>,
|
||||
private val menuOptionsProvider: ObjectMenuOptionsProvider
|
||||
private val menuOptionsProvider: ObjectMenuOptionsProvider,
|
||||
private val addObjectToCollection: AddObjectToCollection
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectMenuViewModel(
|
||||
|
@ -310,7 +314,8 @@ class ObjectMenuViewModel(
|
|||
dispatcher = dispatcher,
|
||||
updateFields = updateFields,
|
||||
delegator = delegator,
|
||||
menuOptionsProvider = menuOptionsProvider
|
||||
menuOptionsProvider = menuOptionsProvider,
|
||||
addObjectToCollection = addObjectToCollection
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package com.anytypeio.anytype.presentation.objects.menu
|
||||
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.domain.`object`.DuplicateObject
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.AddToFavorite
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.RemoveFromFavorite
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -40,7 +43,8 @@ abstract class ObjectMenuViewModelBase(
|
|||
protected val dispatcher: Dispatcher<Payload>,
|
||||
private val analytics: Analytics,
|
||||
private val menuOptionsProvider: ObjectMenuOptionsProvider,
|
||||
private val duplicateObject: DuplicateObject
|
||||
private val duplicateObject: DuplicateObject,
|
||||
private val addObjectToCollection: AddObjectToCollection
|
||||
) : BaseViewModel() {
|
||||
|
||||
protected val jobs = mutableListOf<Job>()
|
||||
|
@ -168,6 +172,74 @@ abstract class ObjectMenuViewModelBase(
|
|||
}
|
||||
}
|
||||
|
||||
fun onBackLinkOrAddToObjectAction(
|
||||
ctx: Id,
|
||||
backLinkId: Id,
|
||||
backLinkName: String,
|
||||
backLinkLayout: ObjectType.Layout?,
|
||||
backLinkIcon: ObjectIcon,
|
||||
fromName: String
|
||||
) {
|
||||
Timber.e("onBackLinkOrAddToObjectAction, ctx:[$ctx], backLinkId:[$backLinkId], backLinkName:[$backLinkName], backLinkLayout:[$backLinkLayout], fromName:[$fromName]")
|
||||
if (backLinkLayout == null) {
|
||||
Timber.e("onBackLinkOrAddToObjectAction, layout is null")
|
||||
viewModelScope.launch { _toasts.emit(BACK_LINK_WRONG_LAYOUT) }
|
||||
return
|
||||
}
|
||||
when (backLinkLayout) {
|
||||
ObjectType.Layout.BASIC,
|
||||
ObjectType.Layout.PROFILE,
|
||||
ObjectType.Layout.TODO,
|
||||
ObjectType.Layout.NOTE -> {
|
||||
onLinkedMyselfTo(
|
||||
myself = ctx, addTo = backLinkId, fromName = fromName
|
||||
)
|
||||
}
|
||||
ObjectType.Layout.COLLECTION -> {
|
||||
proceedWithAddObjectToCollection(
|
||||
ctx = ctx,
|
||||
collection = backLinkId,
|
||||
collectionName = backLinkName,
|
||||
collectionIcon = backLinkIcon,
|
||||
fromName = fromName
|
||||
)
|
||||
}
|
||||
else -> { Timber.e("onBackLinkOrAddToObjectAction, layout:$backLinkLayout is not supported") }
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithAddObjectToCollection(
|
||||
ctx: Id,
|
||||
collection: Id,
|
||||
collectionName: String,
|
||||
collectionIcon: ObjectIcon,
|
||||
fromName: String
|
||||
) {
|
||||
val params = AddObjectToCollection.Params(
|
||||
ctx = collection,
|
||||
after = "",
|
||||
targets = listOf(ctx)
|
||||
)
|
||||
viewModelScope.launch {
|
||||
addObjectToCollection.execute(params).fold(
|
||||
onSuccess = { payload ->
|
||||
dispatcher.send(payload)
|
||||
sendAnalyticsObjectLinkToEvent(analytics)
|
||||
commands.emit(
|
||||
Command.OpenSnackbar(
|
||||
id = collection,
|
||||
currentObjectName = fromName,
|
||||
targetObjectName = collectionName,
|
||||
icon = collectionIcon,
|
||||
isCollection = true
|
||||
)
|
||||
)
|
||||
},
|
||||
onFailure = { Timber.e(it, "Error while adding object to collection") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onLinkedMyselfTo(myself: Id, addTo: Id, fromName: String?) {
|
||||
Timber.d("onLinkedMyselfTo, myself:[$myself], addTo:[$addTo], fromName:[$fromName]")
|
||||
jobs += viewModelScope.launch {
|
||||
|
@ -203,6 +275,13 @@ abstract class ObjectMenuViewModelBase(
|
|||
}
|
||||
}
|
||||
|
||||
fun proceedWithOpeningCollection(id: Id) {
|
||||
Timber.d("proceedWithOpeningCollection, id:[$id]")
|
||||
viewModelScope.launch {
|
||||
delegator.delegate(Action.OpenCollection(id))
|
||||
}
|
||||
}
|
||||
|
||||
fun proceedWithDuplication(ctx: Id) {
|
||||
Timber.d("proceedWithDuplication, ctx:[$ctx]")
|
||||
viewModelScope.launch {
|
||||
|
@ -238,7 +317,8 @@ abstract class ObjectMenuViewModelBase(
|
|||
val id: Id,
|
||||
val currentObjectName: String?,
|
||||
val targetObjectName: String?,
|
||||
val icon: ObjectIcon
|
||||
val icon: ObjectIcon,
|
||||
val isCollection: Boolean = false
|
||||
) : Command()
|
||||
}
|
||||
|
||||
|
@ -253,5 +333,6 @@ abstract class ObjectMenuViewModelBase(
|
|||
const val OBJECT_IS_LOCKED_MSG = "Your object is locked"
|
||||
const val OBJECT_IS_UNLOCKED_MSG = "Your object is locked"
|
||||
const val SOMETHING_WENT_WRONG_MSG = "Something went wrong. Please, try again later."
|
||||
const val BACK_LINK_WRONG_LAYOUT = "Wrong object layout, try again"
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import com.anytypeio.anytype.analytics.base.Analytics
|
|||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.AddToFavorite
|
||||
import com.anytypeio.anytype.domain.dashboard.interactor.RemoveFromFavorite
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -34,6 +35,7 @@ class ObjectSetMenuViewModel(
|
|||
menuOptionsProvider: ObjectMenuOptionsProvider,
|
||||
private val objectState: StateFlow<ObjectState>,
|
||||
private val analytics: Analytics,
|
||||
private val addObjectToCollection: AddObjectToCollection
|
||||
) : ObjectMenuViewModelBase(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
addToFavorite = addToFavorite,
|
||||
|
@ -45,6 +47,7 @@ class ObjectSetMenuViewModel(
|
|||
dispatcher = dispatcher,
|
||||
analytics = analytics,
|
||||
menuOptionsProvider = menuOptionsProvider,
|
||||
addObjectToCollection = addObjectToCollection
|
||||
) {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -60,6 +63,7 @@ class ObjectSetMenuViewModel(
|
|||
private val analytics: Analytics,
|
||||
private val objectState: StateFlow<ObjectState>,
|
||||
private val menuOptionsProvider: ObjectMenuOptionsProvider,
|
||||
private val addObjectToCollection: AddObjectToCollection
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectSetMenuViewModel(
|
||||
|
@ -73,7 +77,8 @@ class ObjectSetMenuViewModel(
|
|||
analytics = analytics,
|
||||
objectState = objectState,
|
||||
dispatcher = dispatcher,
|
||||
menuOptionsProvider = menuOptionsProvider
|
||||
menuOptionsProvider = menuOptionsProvider,
|
||||
addObjectToCollection = addObjectToCollection
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.anytypeio.anytype.core_models.Marketplace.MARKETPLACE_ID
|
|||
import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.AUDIO
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.BOOKMARK
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.DASHBOARD
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.DATE
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.FILE
|
||||
|
@ -17,6 +18,8 @@ import com.anytypeio.anytype.core_models.ObjectTypeIds.IMAGE
|
|||
import com.anytypeio.anytype.core_models.ObjectTypeIds.OBJECT_TYPE
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.RELATION
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.RELATION_OPTION
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.SET
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.SPACE
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.TEMPLATE
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.VIDEO
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
|
@ -426,6 +429,62 @@ object ObjectSearchConstants {
|
|||
)
|
||||
//endregion
|
||||
|
||||
//region BACK LINK OR ADD TO OBJECT
|
||||
fun filtersBackLinkOrAddToObject(ignore: Id?, workspaceId: String) = listOf(
|
||||
DVFilter(
|
||||
relation = Relations.IS_ARCHIVED,
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = true
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.IS_HIDDEN,
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = true
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.IS_DELETED,
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = true
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.TYPE,
|
||||
condition = DVFilterCondition.NOT_IN,
|
||||
value = listOf(
|
||||
OBJECT_TYPE,
|
||||
RELATION,
|
||||
TEMPLATE,
|
||||
IMAGE,
|
||||
FILE,
|
||||
VIDEO,
|
||||
AUDIO,
|
||||
DASHBOARD,
|
||||
DATE,
|
||||
RELATION_OPTION,
|
||||
SPACE,
|
||||
SET,
|
||||
BOOKMARK
|
||||
)
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.ID,
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = ignore
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.WORKSPACE_ID,
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = workspaceId
|
||||
)
|
||||
)
|
||||
|
||||
val sortBackLinkOrAddToObject = listOf(
|
||||
DVSort(
|
||||
relationKey = Relations.LAST_MODIFIED_DATE,
|
||||
type = DVSortType.DESC
|
||||
)
|
||||
)
|
||||
//endregion
|
||||
|
||||
val defaultKeys = listOf(
|
||||
Relations.ID,
|
||||
Relations.NAME,
|
||||
|
|
|
@ -230,6 +230,7 @@ class ObjectSetViewModel(
|
|||
proceedWithSettingUnsplashImage(action)
|
||||
}
|
||||
is Action.OpenObject -> proceedWithOpeningObject(action.id)
|
||||
is Action.OpenCollection -> proceedWithOpeningObjectCollection(action.id)
|
||||
is Action.Duplicate -> proceedWithNavigation(
|
||||
target = action.id,
|
||||
layout = ObjectType.Layout.SET
|
||||
|
@ -1085,6 +1086,21 @@ class ObjectSetViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithOpeningObjectCollection(target: Id) {
|
||||
isCustomizeViewPanelVisible.value = false
|
||||
jobs += viewModelScope.launch {
|
||||
closeBlock.execute(context).fold(
|
||||
onSuccess = {
|
||||
navigate(EventWrapper(AppNavigation.Command.OpenSetOrCollection(target = target)))
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while closing object set: $context")
|
||||
navigate(EventWrapper(AppNavigation.Command.OpenSetOrCollection(target = target)))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithNavigation(target: Id, layout: ObjectType.Layout?) {
|
||||
when (layout) {
|
||||
ObjectType.Layout.BASIC,
|
||||
|
@ -1097,11 +1113,11 @@ class ObjectSetViewModel(
|
|||
ObjectType.Layout.SET, ObjectType.Layout.COLLECTION -> {
|
||||
closeBlock.execute(context).fold(
|
||||
onSuccess = {
|
||||
navigate(EventWrapper(AppNavigation.Command.OpenObjectSet(target)))
|
||||
navigate(EventWrapper(AppNavigation.Command.OpenSetOrCollection(target)))
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while closing object set: $context")
|
||||
navigate(EventWrapper(AppNavigation.Command.OpenObjectSet(target)))
|
||||
navigate(EventWrapper(AppNavigation.Command.OpenSetOrCollection(target)))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -491,7 +491,7 @@ abstract class RelationValueBaseViewModel(
|
|||
}
|
||||
ObjectType.Layout.SET -> {
|
||||
viewModelScope.launch {
|
||||
navigation.emit(AppNavigation.Command.OpenObjectSet(id))
|
||||
navigation.emit(AppNavigation.Command.OpenSetOrCollection(id))
|
||||
}
|
||||
}
|
||||
else -> Timber.d("Unexpected layout: $layout").also {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue