1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

Objects | Feature | Navigate to set of this type or create a new set (#1880)

This commit is contained in:
Evgenii Kozlov 2021-10-28 21:22:46 +03:00 committed by GitHub
parent 6af7da4881
commit d5ee41c7a5
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 354 additions and 85 deletions

View file

@ -13,6 +13,7 @@ import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.*
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.clipboard.Clipboard
import com.anytypeio.anytype.domain.clipboard.Copy
@ -34,6 +35,7 @@ import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.page.navigation.GetListPages
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.status.ThreadStatusChannel
import com.anytypeio.anytype.mocking.MockDataFactory
@ -129,6 +131,9 @@ open class EditorTestSetup {
lateinit var getDefaultEditorType: GetDefaultEditorType
private lateinit var findObjectSetForType: FindObjectSetForType
private lateinit var createObjectSet: CreateObjectSet
@Mock
lateinit var updateDivider: UpdateDivider
@ -227,6 +232,8 @@ open class EditorTestSetup {
updateDetail = UpdateDetail(repo)
getCompatibleObjectTypes = GetCompatibleObjectTypes(repo)
getDefaultEditorType = GetDefaultEditorType(userSettingsRepository)
createObjectSet = CreateObjectSet(repo)
findObjectSetForType = FindObjectSetForType(repo)
TestEditorFragment.testViewModelFactory = EditorViewModelFactory(
openPage = openPage,
@ -296,7 +303,9 @@ open class EditorTestSetup {
getCompatibleObjectTypes = getCompatibleObjectTypes,
objectTypesProvider = objectTypesProvider,
searchObjects = getSearchObjects,
getDefaultEditorType = getDefaultEditorType
getDefaultEditorType = getDefaultEditorType,
createObjectSet = createObjectSet,
findObjectSetForType = findObjectSetForType
)
}

View file

@ -7,6 +7,7 @@ import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.Gateway
@ -157,15 +158,17 @@ abstract class TestObjectSetSetup {
relations: List<Relation> = emptyList()
) {
repo.stub {
onBlocking { openObjectSet(ctx) } doReturn Payload(
context = ctx,
events = listOf(
Event.Command.ShowObject(
context = ctx,
root = ctx,
details = details,
blocks = set,
relations = relations
onBlocking { openObjectSet(ctx) } doReturn Result.Success(
Payload(
context = ctx,
events = listOf(
Event.Command.ShowObject(
context = ctx,
root = ctx,
details = details,
blocks = set,
relations = relations
)
)
)
)
@ -183,23 +186,25 @@ abstract class TestObjectSetSetup {
objectTypes: List<ObjectType>
) {
repo.stub {
onBlocking { openObjectSet(ctx) } doReturn Payload(
context = ctx,
events = listOf(
Event.Command.ShowObject(
context = ctx,
root = ctx,
details = details,
blocks = set,
relations = relations,
objectTypes = objectTypes
),
Event.Command.DataView.SetRecords(
context = ctx,
id = dataview,
view = viewer,
total = total,
records = records
onBlocking { openObjectSet(ctx) } doReturn Result.Success(
Payload(
context = ctx,
events = listOf(
Event.Command.ShowObject(
context = ctx,
root = ctx,
details = details,
blocks = set,
relations = relations,
objectTypes = objectTypes
),
Event.Command.DataView.SetRecords(
context = ctx,
id = dataview,
view = viewer,
total = total,
records = records
)
)
)
)
@ -207,7 +212,7 @@ abstract class TestObjectSetSetup {
}
fun launchFragment(args: Bundle): FragmentScenario<TestObjectSetFragment> {
return launchFragmentInContainer<TestObjectSetFragment>(
return launchFragmentInContainer(
fragmentArgs = args,
themeResId = R.style.AppTheme
)

View file

@ -15,6 +15,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.*
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.block.interactor.sets.GetObjectTypes
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.clipboard.Clipboard
@ -35,6 +36,7 @@ import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.relations.AddFileToObject
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.status.ThreadStatusChannel
import com.anytypeio.anytype.presentation.editor.DocumentExternalEventReducer
@ -131,6 +133,7 @@ object EditorSessionModule {
removeLinkMark: RemoveLinkMark,
createPage: CreatePage,
createDocument: CreateDocument,
createObjectSet: CreateObjectSet,
createObject: CreateObject,
createNewDocument: CreateNewDocument,
documentExternalEventReducer: DocumentExternalEventReducer,
@ -145,7 +148,8 @@ object EditorSessionModule {
getCompatibleObjectTypes: GetCompatibleObjectTypes,
objectTypesProvider: ObjectTypesProvider,
searchObjects: SearchObjects,
getDefaultEditorType: GetDefaultEditorType
getDefaultEditorType: GetDefaultEditorType,
findObjectSetForType: FindObjectSetForType
): EditorViewModelFactory = EditorViewModelFactory(
openPage = openPage,
closeObject = closePage,
@ -169,7 +173,9 @@ object EditorSessionModule {
getCompatibleObjectTypes = getCompatibleObjectTypes,
objectTypesProvider = objectTypesProvider,
searchObjects = searchObjects,
getDefaultEditorType = getDefaultEditorType
getDefaultEditorType = getDefaultEditorType,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet
)
@JvmStatic
@ -734,4 +740,18 @@ object EditorUseCaseModule {
fun provideGetCompatibleObjectTypesUseCase(
repository: BlockRepository
): GetCompatibleObjectTypes = GetCompatibleObjectTypes(repository)
@JvmStatic
@Provides
@PerScreen
fun findObjectSetForType(
repo: BlockRepository
): FindObjectSetForType = FindObjectSetForType(repo)
@JvmStatic
@Provides
@PerScreen
fun provideCreateObjectSetUseCase(
repo: BlockRepository
): CreateObjectSet = CreateObjectSet(repo = repo)
}

View file

@ -53,6 +53,7 @@ import com.anytypeio.anytype.core_models.SyncStatus
import com.anytypeio.anytype.core_models.ext.getFirstLinkMarkupParam
import com.anytypeio.anytype.core_models.ext.getSubstring
import com.anytypeio.anytype.core_ui.extensions.addTextFromSelectedStart
import com.anytypeio.anytype.core_ui.extensions.color
import com.anytypeio.anytype.core_ui.extensions.cursorYBottomCoordinate
import com.anytypeio.anytype.core_ui.extensions.isKeyboardVisible
import com.anytypeio.anytype.core_ui.features.editor.*
@ -74,6 +75,7 @@ import com.anytypeio.anytype.ext.extractMarks
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.EditorViewModelFactory
import com.anytypeio.anytype.presentation.editor.Snack
import com.anytypeio.anytype.presentation.editor.editor.*
import com.anytypeio.anytype.presentation.editor.editor.actions.ActionItemType
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
@ -102,6 +104,7 @@ import com.anytypeio.anytype.ui.relations.RelationAddBaseFragment.Companion.CTX_
import com.anytypeio.anytype.ui.relations.RelationAddToObjectBlockFragment.Companion.RELATION_ADD_RESULT_KEY
import com.anytypeio.anytype.ui.relations.RelationCreateFromScratchForObjectBlockFragment.Companion.RELATION_NEW_RESULT_KEY
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.Snackbar
import com.hbisoft.pickit.PickiT
import com.hbisoft.pickit.PickiTCallbacks
import jp.wasabeef.blurry.Blurry
@ -260,7 +263,7 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
onSlashEvent = vm::onSlashTextWatcherEvent,
onBackPressedCallback = { vm.onBackPressedCallback() },
onKeyPressedEvent = vm::onKeyPressedEvent,
onDragAndDropTrigger = { vh : RecyclerView.ViewHolder -> handleDragAndDropTrigger(vh) },
onDragAndDropTrigger = { vh: RecyclerView.ViewHolder -> handleDragAndDropTrigger(vh) },
onDragListener = dndListener
)
}
@ -317,8 +320,10 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
FirstItemInvisibilityDetector { isVisible ->
if (isVisible) {
topToolbar.setBackgroundColor(0)
topToolbar.statusText.animate().alpha(1f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION).start()
topToolbar.container.animate().alpha(0f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION).start()
topToolbar.statusText.animate().alpha(1f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION)
.start()
topToolbar.container.animate().alpha(0f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION)
.start()
if (blockAdapter.views.isNotEmpty()) {
val firstView = blockAdapter.views.first()
if (firstView is BlockView.Title && firstView.hasCover) {
@ -329,8 +334,10 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
}
} else {
topToolbar.setBackgroundColor(Color.WHITE)
topToolbar.statusText.animate().alpha(0f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION).start()
topToolbar.container.animate().alpha(1f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION).start()
topToolbar.statusText.animate().alpha(0f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION)
.start()
topToolbar.container.animate().alpha(1f).setDuration(DEFAULT_TOOLBAR_ANIM_DURATION)
.start()
topToolbar.setStyle(overCover = false)
}
}
@ -687,7 +694,6 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
vm.navigation.observe(viewLifecycleOwner, navObserver)
vm.controlPanelViewState.observe(viewLifecycleOwner) { render(it) }
vm.commands.observe(viewLifecycleOwner) { execute(it) }
vm.toasts.onEach { toast(it) }.launchIn(lifecycleScope)
vm.searchResultScrollPosition
.filter { it != EditorViewModel.NO_SEARCH_RESULT_POSITION }
.onEach { recycler.smoothScrollToPosition(it) }
@ -708,7 +714,23 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
}.launchIn(lifecycleScope)
with(lifecycleScope) {
subscribe(vm.actions) { blockActionToolbar.bind(it) }
jobs += subscribe(vm.actions) { blockActionToolbar.bind(it) }
jobs += subscribe(vm.toasts) { toast(it) }
jobs += subscribe(vm.snacks) { snack ->
when (snack) {
is Snack.ObjectSetNotFound -> {
Snackbar
.make(
root,
resources.getString(R.string.snack_object_set_not_found),
Snackbar.LENGTH_LONG
)
.setActionTextColor(requireContext().color(R.color.orange))
.setAction(R.string.create_new_set) { vm.onCreateNewSetForType(snack.type) }
.show()
}
}
}
}
}
@ -1026,7 +1048,8 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
}
private fun proceedWithScrollingToActionMenu(command: Command.ScrollToActionMenu): Unit {
val lastSelected = (vm.state.value as ViewState.Success).blocks.indexOfLast { it.id == command.target }
val lastSelected =
(vm.state.value as ViewState.Success).blocks.indexOfLast { it.id == command.target }
if (lastSelected != -1) {
val lm = recycler.layoutManager as LinearLayoutManager
val targetView = lm.findViewByPosition(lastSelected)
@ -1132,7 +1155,8 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
topToolbar.setStyle(overCover = false)
}
}
else -> {}
else -> {
}
}
}
}
@ -1204,7 +1228,8 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
multiSelectTopToolbar.visible()
when {
count > 1 -> {
multiSelectTopToolbar.selectText.text = getString(R.string.number_selected_blocks, count)
multiSelectTopToolbar.selectText.text =
getString(R.string.number_selected_blocks, count)
}
count == 1 -> {
multiSelectTopToolbar.selectText.setText(R.string.one_selected_block)
@ -1968,9 +1993,9 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
//region Drag-and-drop UI logic.
private var dndTargetPos = -1
private var dndTargetPrevious : Pair<Float, Int>? = null
private var dndTargetPrevious: Pair<Float, Int>? = null
var dndTargetLineAnimator : ViewPropertyAnimator? = null
var dndTargetLineAnimator: ViewPropertyAnimator? = null
private var scrollDownJob: Job? = null
private var scrollUpJob: Job? = null

View file

@ -245,4 +245,7 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="object_not_exist">This object doesn\'t exist</string>
<string name="back_to_dashboard">Back to dashboard</string>
<string name="snack_object_set_not_found">Set not found for this type.</string>
<string name="create_new_set">Create</string>
</resources>

View file

@ -27,6 +27,7 @@ object Relations {
const val SNIPPET = "snippet"
const val IS_DRAFT = "isDraft"
const val WORKSPACE_ID = "workspaceId"
const val SET_OF = "setOf"
val defaultRelations = listOf(
ID,

View file

@ -37,8 +37,8 @@ sealed class Response {
sealed class Set : Response() {
data class Create(
val blockId: String,
val targetId: String,
val blockId: Id?,
val targetId: Id,
val payload: Payload
)
}

View file

@ -0,0 +1,25 @@
package com.anytypeio.anytype.core_ui.menu
import android.content.Context
import android.view.View
import android.widget.PopupMenu
import com.anytypeio.anytype.core_ui.R
class ObjectTypePopupMenu(
context: Context,
view: View,
onChangeTypeClicked: () -> Unit,
onOpenSetClicked: () -> Unit
) : PopupMenu(context, view) {
init {
menuInflater.inflate(R.menu.menu_object_type, menu)
setOnMenuItemClickListener { item ->
when(item.itemId) {
R.id.change_type -> onChangeTypeClicked()
R.id.open_set -> onOpenSetClicked()
else -> throw IllegalStateException("Unexpected menu item: $item")
}
true
}
}
}

View file

@ -10,6 +10,8 @@ import androidx.constraintlayout.helper.widget.Flow
import androidx.constraintlayout.widget.ConstraintLayout
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.menu.DataViewEditViewPopupMenu
import com.anytypeio.anytype.core_ui.menu.ObjectTypePopupMenu
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.setDrawableColor
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
@ -30,7 +32,10 @@ class FeaturedRelationGroupWidget : ConstraintLayout {
private val defaultTextSize: Float = context.dimen(R.dimen.sp_13)
private val dividerSize: Int = context.dimen(R.dimen.dp_4).toInt()
fun set(item: BlockView.FeaturedRelation, click: (ListenerType.Relation) -> Unit) {
fun set(
item: BlockView.FeaturedRelation,
click: (ListenerType.Relation) -> Unit
) {
clear()
val flow = Flow(context).apply {
@ -188,7 +193,17 @@ class FeaturedRelationGroupWidget : ConstraintLayout {
setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize)
}
view.setOnClickListener {
click.invoke(ListenerType.Relation.ObjectType(type = relation.relationId))
val popup = ObjectTypePopupMenu(
context = context,
view = it,
onChangeTypeClicked = {
click(ListenerType.Relation.ObjectType(type = relation.relationId))
},
onOpenSetClicked = {
click(ListenerType.Relation.ObjectTypeOpenSet(type = relation.type))
}
)
popup.show()
}
addView(view)
ids.add(view.id)

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/change_type"
android:title="@string/change_type" />
<item
android:id="@+id/open_set"
android:title="@string/open_set" />
</menu>

View file

@ -436,5 +436,6 @@
<string name="name_type_page_icon">📄</string>
<string name="name_type_page_subtitle">Proto type to start with</string>
<string name="non_existent_object">Non-existent object</string>
<string name="open_set">Open set</string>
</resources>

View file

@ -257,7 +257,7 @@ class BlockDataRepository(
override suspend fun createSet(
context: Id,
target: Id?,
position: Position,
position: Position?,
objectType: String?
): CreateObjectSet.Response {
val result = factory.remote.createSet(

View file

@ -63,7 +63,7 @@ interface BlockDataStore {
suspend fun createSet(
contextId: String,
targetId: String?,
position: Position,
position: Position?,
objectType: String?
): Response.Set.Create

View file

@ -64,7 +64,7 @@ interface BlockRemote {
suspend fun createSet(
contextId: String,
targetId: String?,
position: Position,
position: Position?,
objectType: String?
): Response.Set.Create

View file

@ -178,7 +178,7 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
override suspend fun createSet(
contextId: String,
targetId: String?,
position: Position,
position: Position?,
objectType: String?
): Response.Set.Create = remote.createSet(
contextId = contextId,

View file

@ -1,34 +1,39 @@
package com.anytypeio.anytype.domain.block.interactor.sets
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class CreateObjectSet(private val repo: BlockRepository) :
BaseUseCase<CreateObjectSet.Response, CreateObjectSet.Params>() {
class CreateObjectSet(
private val repo: BlockRepository
) : BaseUseCase<CreateObjectSet.Response, CreateObjectSet.Params>() {
override suspend fun run(params: Params) = safe {
repo.createSet(
context = params.context,
context = params.ctx,
target = params.target,
position = params.position,
objectType = params.objectType
objectType = params.type
)
}
data class Params(
val context: Id,
val ctx: Id,
val target: Id? = null,
val position: Position = Position.BOTTOM,
val objectType: Url? = null
val position: Position? = null,
val type: Id? = null
)
/**
* @property [target] id of the new set
* @property [block] optional id of the link block pointing to the new set.
*/
data class Response(
val block: Id,
val target: Id,
val block: Id?,
val payload: Payload
)
}

View file

@ -116,7 +116,7 @@ interface BlockRepository {
suspend fun createSet(
context: Id,
target: Id? = null,
position: Position = Position.BOTTOM,
position: Position? = null,
objectType: String? = null
): CreateObjectSet.Response

View file

@ -0,0 +1,65 @@
package com.anytypeio.anytype.domain.sets
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.sets.FindObjectSetForType.Params
import com.anytypeio.anytype.domain.sets.FindObjectSetForType.Response
/**
* Use-case for finding an object set for a type.
* @see Response for details.
* @see Params for details.
*/
class FindObjectSetForType(
private val repo: BlockRepository
) : BaseUseCase<Response, Params>() {
override suspend fun run(params: Params) = safe {
val results = repo.searchObjects(
limit = 1,
filters = listOf(
DVFilter(
relationKey = Relations.TYPE,
condition = DVFilterCondition.EQUAL,
value = ObjectType.SET_URL
),
DVFilter(
relationKey = Relations.SET_OF,
condition = DVFilterCondition.IN,
value = listOf(params.type)
)
),
sorts = emptyList(),
fulltext = "",
offset = 0
)
if (results.isNotEmpty()) {
val obj = ObjectWrapper.Basic(results.first())
check(obj.layout == ObjectType.Layout.SET) { "Unexpected layout for set" }
Response.Success(
type = params.type,
obj = obj
)
} else {
Response.NotFound(type = params.type)
}
}
/**
* @property [type] object type id
*/
data class Params(val type: Id)
sealed class Response {
/**
* Could not found a set for this [type].
*/
data class NotFound(val type: Id) : Response()
/**
* Found a set for this [type].
*/
data class Success(val type: Id, val obj: ObjectWrapper.Basic) : Response()
}
}

View file

@ -204,10 +204,13 @@ class BlockMiddleware(
override suspend fun createSet(
contextId: String,
targetId: String?,
position: Position,
position: Position?,
objectType: String?
): Response.Set.Create = middleware.createSet(
contextId, targetId, position, objectType
contextId = contextId,
targetId = targetId,
position = position,
objectType = objectType
)
override suspend fun setActiveDataViewViewer(

View file

@ -970,7 +970,7 @@ class Middleware(
fun createSet(
contextId: String,
targetId: String?,
position: Position,
position: Position?,
objectType: String?
): Response.Set.Create {
@ -984,7 +984,7 @@ class Middleware(
contextId = contextId,
targetId = targetId.orEmpty(),
source = source,
position = position.toMiddlewareModel()
position = position?.toMiddlewareModel() ?: Block.Position.Bottom
)
if (BuildConfig.DEBUG) logRequest(request)
@ -994,7 +994,7 @@ class Middleware(
if (BuildConfig.DEBUG) logResponse(response)
return Response.Set.Create(
blockId = response.blockId,
blockId = response.blockId.ifEmpty { null },
targetId = response.targetId,
payload = response.event.toPayload()
)

View file

@ -38,6 +38,7 @@ import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.interactor.RemoveLinkMark
import com.anytypeio.anytype.domain.block.interactor.UpdateLinkMarks
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.editor.Editor
@ -47,6 +48,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.common.StateReducer
@ -137,7 +139,9 @@ class EditorViewModel(
private val getCompatibleObjectTypes: GetCompatibleObjectTypes,
private val objectTypesProvider: ObjectTypesProvider,
private val searchObjects: SearchObjects,
private val getDefaultEditorType: GetDefaultEditorType
private val getDefaultEditorType: GetDefaultEditorType,
private val findObjectSetForType: FindObjectSetForType,
private val createObjectSet: CreateObjectSet
) : ViewStateViewModel<ViewState>(),
SupportNavigation<EventWrapper<AppNavigation.Command>>,
SupportCommand<Command>,
@ -198,6 +202,8 @@ class EditorViewModel(
private val _toasts: Channel<String> = Channel()
val toasts: Flow<String> get() = _toasts.consumeAsFlow()
val snacks = MutableSharedFlow<Snack>(replay = 0)
/**
* Open gallery and search media files for block with that id
*/
@ -2656,10 +2662,6 @@ class EditorViewModel(
proceedWithClearingFocus()
val details = orchestrator.stores.details.current()
val wrapper = ObjectWrapper.Basic(map = details.details[target]?.map ?: emptyMap())
if (wrapper.isDeleted == true) {
proceedWithOpeningDeletedPage(target)
return
}
when (wrapper.layout) {
ObjectType.Layout.BASIC,
ObjectType.Layout.PROFILE,
@ -2679,10 +2681,6 @@ class EditorViewModel(
}
}
private fun proceedWithOpeningDeletedPage(target: Id) {
navigate(EventWrapper(AppNavigation.Command.OpenObject(target)))
}
fun onAddNewObjectClicked(type: String, layout: ObjectType.Layout) {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
@ -3530,6 +3528,23 @@ class EditorViewModel(
eventName = EventsDictionary.POPUP_OBJECT_TYPE_CHANGE
)
}
is ListenerType.Relation.ObjectTypeOpenSet -> {
viewModelScope.launch {
findObjectSetForType(FindObjectSetForType.Params(clicked.type)).process(
failure = { Timber.e(it, "Error while search for a set for this type") },
success = { response ->
when(response) {
is FindObjectSetForType.Response.NotFound -> {
snacks.emit(Snack.ObjectSetNotFound(clicked.type))
}
is FindObjectSetForType.Response.Success -> {
proceedWithOpeningSet(response.obj.id)
}
}
}
)
}
}
}
}
@ -3680,7 +3695,17 @@ class EditorViewModel(
analytics = analytics,
eventName = EventsDictionary.SCREEN_DOCUMENT
)
navigate(EventWrapper(AppNavigation.Command.OpenObject(target)))
viewModelScope.launch {
closePage(CloseBlock.Params(context)).process(
failure = {
Timber.e(it, "Error while closing object")
navigate(EventWrapper(AppNavigation.Command.OpenObject(target)))
},
success = {
navigate(EventWrapper(AppNavigation.Command.OpenObject(target)))
}
)
}
}
private fun proceedWithOpeningSet(target: Id) {
@ -3688,7 +3713,17 @@ class EditorViewModel(
analytics = analytics,
eventName = EventsDictionary.SCREEN_SET
)
navigate(EventWrapper(AppNavigation.Command.OpenObjectSet(target)))
viewModelScope.launch {
closePage(CloseBlock.Params(context)).process(
failure = {
Timber.e(it, "Error while closing object")
navigate(EventWrapper(AppNavigation.Command.OpenObjectSet(target)))
},
success = {
navigate(EventWrapper(AppNavigation.Command.OpenObjectSet(target)))
}
)
}
}
/**
@ -3790,7 +3825,6 @@ class EditorViewModel(
const val CANNOT_OPEN_STYLE_PANEL_FOR_DESCRIPTION = "Description block is text primitive and therefore no styling can be applied."
const val CANNOT_OPEN_STYLE_PANEL_FOR_CODE_BLOCK_ERROR =
"Opening style panel for code block currently not supported"
const val FLAVOUR_EXPERIMENTAL = "experimental"
const val ERROR_UNSUPPORTED_BEHAVIOR = "Currently unsupported behavior."
const val NOT_ALLOWED_FOR_OBJECT = "Not allowed for this object"
@ -5324,4 +5358,18 @@ class EditorViewModel(
}
}
//endregion
fun onCreateNewSetForType(type: Id) {
viewModelScope.launch {
createObjectSet(
CreateObjectSet.Params(
ctx = context,
type = type
)
).process(
failure = { Timber.e(it, "Error while creating a set of type: $type") },
success = { response -> proceedWithOpeningSet(response.target) }
)
}
}
}

View file

@ -10,6 +10,7 @@ import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.block.interactor.RemoveLinkMark
import com.anytypeio.anytype.domain.block.interactor.UpdateLinkMarks
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
@ -17,6 +18,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
@ -29,6 +31,7 @@ open class EditorViewModelFactory(
private val closeObject: CloseBlock,
private val createPage: CreatePage,
private val createDocument: CreateDocument,
private val createObjectSet: CreateObjectSet,
private val createObject: CreateObject,
private val createNewDocument: CreateNewDocument,
private val setObjectIsArchived: SetObjectIsArchived,
@ -47,7 +50,8 @@ open class EditorViewModelFactory(
private val getCompatibleObjectTypes: GetCompatibleObjectTypes,
private val objectTypesProvider: ObjectTypesProvider,
private val searchObjects: SearchObjects,
private val getDefaultEditorType: GetDefaultEditorType
private val getDefaultEditorType: GetDefaultEditorType,
private val findObjectSetForType: FindObjectSetForType
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -75,7 +79,9 @@ open class EditorViewModelFactory(
getCompatibleObjectTypes = getCompatibleObjectTypes,
objectTypesProvider = objectTypesProvider,
searchObjects = searchObjects,
getDefaultEditorType = getDefaultEditorType
getDefaultEditorType = getDefaultEditorType,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet
) as T
}
}

View file

@ -0,0 +1,7 @@
package com.anytypeio.anytype.presentation.editor
import com.anytypeio.anytype.core_models.Id
sealed class Snack {
data class ObjectSetNotFound(val type: Id) : Snack()
}

View file

@ -58,6 +58,7 @@ sealed class ListenerType {
data class Placeholder(val target: Id) : Relation()
data class Related(val value: BlockView.Relation) : Relation()
data class ObjectType(val type: String) : Relation()
data class ObjectTypeOpenSet(val type: String) : Relation()
data class Featured(val relation: DocumentRelationView) : Relation()
}
}

View file

@ -16,6 +16,7 @@ sealed class CreateSetViewState {
data class AddObjectType(val data: ArrayList<CreateObjectTypeView>) : CreateSetViewState()
}
@Deprecated("LEGACY SUSPECT")
class CreateObjectSetViewModel(
private val createObjectSet: CreateObjectSet,
private val getObjectTypes: GetObjectTypes,
@ -98,8 +99,8 @@ class CreateObjectSetViewModel(
viewModelScope.launch {
createObjectSet(
CreateObjectSet.Params(
context = context,
objectType = type
ctx = context,
type = type
)
).process(
failure = { Timber.e(it, "Error while creating new set for type: $type") },

View file

@ -12,6 +12,7 @@ import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.*
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.clipboard.Copy
import com.anytypeio.anytype.domain.clipboard.Paste
@ -26,6 +27,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.presentation.MockBlockFactory
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
@ -218,6 +220,12 @@ open class EditorViewModelTest {
@Mock
lateinit var getDefaultEditorType: GetDefaultEditorType
@Mock
lateinit var findObjectSetForType: FindObjectSetForType
@Mock
lateinit var createObjectSet: CreateObjectSet
private lateinit var updateDetail: UpdateDetail
lateinit var vm: EditorViewModel
@ -3982,7 +3990,9 @@ open class EditorViewModelTest {
updateDetail = updateDetail,
getCompatibleObjectTypes = getCompatibleObjectTypes,
objectTypesProvider = objectTypesProvider,
searchObjects = searchObjects
searchObjects = searchObjects,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet
)
}

View file

@ -11,6 +11,7 @@ import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.*
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.clipboard.Copy
import com.anytypeio.anytype.domain.clipboard.Paste
@ -25,6 +26,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.presentation.editor.DocumentExternalEventReducer
import com.anytypeio.anytype.presentation.editor.Editor
@ -188,6 +190,12 @@ open class EditorPresentationTestSetup {
@Mock
lateinit var getDefaultEditorType: GetDefaultEditorType
@Mock
lateinit var findObjectSetForType: FindObjectSetForType
@Mock
lateinit var createObjectSet: CreateObjectSet
private val builder: UrlBuilder get() = UrlBuilder(gateway)
private lateinit var updateDetail: UpdateDetail
@ -271,7 +279,9 @@ open class EditorPresentationTestSetup {
getCompatibleObjectTypes = getCompatibleObjectTypes,
objectTypesProvider = objectTypesProvider,
searchObjects = searchObjects,
getDefaultEditorType = getDefaultEditorType
getDefaultEditorType = getDefaultEditorType,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet
)
}