mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Editor | Feature | Add Date objects as mentions (#1822)
* object search use case DI * link to filter and sorts * fixes * di fix * fix * add support layout * fixes * fix * fixes * fixes * fixes * ci * pr fix * pr fix
This commit is contained in:
parent
5f7cbc8b3c
commit
1e369208d7
24 changed files with 358 additions and 164 deletions
|
@ -2,7 +2,6 @@ package com.anytypeio.anytype.di.feature
|
|||
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerDialog
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.relations.RelationObjectValueAddViewModel
|
||||
|
@ -42,11 +41,4 @@ object AddObjectRelationObjectValueModule {
|
|||
RelationObjectValueAddViewModel.Factory(
|
||||
relations, values, searchObjects, urlBuilder, objectTypesProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun provideSearchObjectsUseCase(
|
||||
repo: BlockRepository
|
||||
): SearchObjects = SearchObjects(repo = repo)
|
||||
}
|
|
@ -19,6 +19,7 @@ import com.anytypeio.anytype.domain.clipboard.Clipboard
|
|||
import com.anytypeio.anytype.domain.clipboard.Copy
|
||||
import com.anytypeio.anytype.domain.clipboard.Paste
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SetRelationKey
|
||||
import com.anytypeio.anytype.domain.download.DownloadFile
|
||||
import com.anytypeio.anytype.domain.download.Downloader
|
||||
|
@ -29,7 +30,6 @@ 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.page.navigation.GetListPages
|
||||
import com.anytypeio.anytype.domain.relations.AddFileToObject
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
import com.anytypeio.anytype.domain.status.ThreadStatusChannel
|
||||
|
@ -135,13 +135,13 @@ object EditorSessionModule {
|
|||
renderer: DefaultBlockViewRenderer,
|
||||
setObjectIsArchived: SetObjectIsArchived,
|
||||
orchestrator: Orchestrator,
|
||||
getListPages: GetListPages,
|
||||
analytics: Analytics,
|
||||
dispatcher: Dispatcher<Payload>,
|
||||
detailModificationManager: DetailModificationManager,
|
||||
updateDetail: UpdateDetail,
|
||||
getCompatibleObjectTypes: GetCompatibleObjectTypes,
|
||||
objectTypesProvider: ObjectTypesProvider
|
||||
objectTypesProvider: ObjectTypesProvider,
|
||||
searchObjects: SearchObjects
|
||||
): EditorViewModelFactory = EditorViewModelFactory(
|
||||
openPage = openPage,
|
||||
closeObject = closePage,
|
||||
|
@ -158,13 +158,13 @@ object EditorSessionModule {
|
|||
renderer = renderer,
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
orchestrator = orchestrator,
|
||||
getListPages = getListPages,
|
||||
analytics = analytics,
|
||||
dispatcher = dispatcher,
|
||||
detailModificationManager = detailModificationManager,
|
||||
updateDetail = updateDetail,
|
||||
getCompatibleObjectTypes = getCompatibleObjectTypes,
|
||||
objectTypesProvider = objectTypesProvider
|
||||
objectTypesProvider = objectTypesProvider,
|
||||
searchObjects = searchObjects
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
@ -291,11 +291,6 @@ object EditorSessionModule {
|
|||
@Module
|
||||
object EditorUseCaseModule {
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun getListPages(repo: BlockRepository): GetListPages = GetListPages(repo = repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
|
@ -721,4 +716,11 @@ object EditorUseCaseModule {
|
|||
fun provideGetCompatibleObjectTypesUseCase(
|
||||
repository: BlockRepository
|
||||
): GetCompatibleObjectTypes = GetCompatibleObjectTypes(repository)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun searchObjects(
|
||||
repo: BlockRepository
|
||||
): SearchObjects = SearchObjects(repo = repo)
|
||||
}
|
|
@ -5,6 +5,7 @@ import com.anytypeio.anytype.core_models.Block
|
|||
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.core_utils.di.scope.PerDialog
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.feature.relations.RelationAddToDataViewSubComponent
|
||||
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromScratchForDataViewSubComponent
|
||||
|
@ -269,4 +270,11 @@ object ObjectSetModule {
|
|||
): SetObjectIsArchived = SetObjectIsArchived(
|
||||
repo = repo
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideSearchObjectsUseCase(
|
||||
repo: BlockRepository
|
||||
): SearchObjects = SearchObjects(repo = repo)
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerDialog
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.relations.RelationFileValueAddViewModel
|
||||
|
@ -40,11 +39,4 @@ object RelationFileValueAddModule {
|
|||
RelationFileValueAddViewModel.Factory(
|
||||
relations, values, searchObjects, urlBuilder
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun provideSearchObjectsUseCase(
|
||||
repo: BlockRepository
|
||||
): SearchObjects = SearchObjects(repo = repo)
|
||||
}
|
|
@ -3,7 +3,6 @@ package com.anytypeio.anytype.di.feature.sets;
|
|||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerModal
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.UpdateDataViewViewer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -55,11 +54,4 @@ object CreateFilterModule {
|
|||
urlBuilder = urlBuilder,
|
||||
objectTypesProvider = objectTypesProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
fun provideSearchObjectsUseCase(
|
||||
repo: BlockRepository
|
||||
): SearchObjects = SearchObjects(repo = repo)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.anytypeio.anytype.di.feature.sets;
|
|||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerModal
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.UpdateDataViewViewer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -55,11 +54,4 @@ object ModifyFilterModule {
|
|||
urlBuilder = urlBuilder,
|
||||
objectTypesProvider = objectTypesProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
fun provideSearchObjectsUseCase(
|
||||
repo: BlockRepository
|
||||
): SearchObjects = SearchObjects(repo = repo)
|
||||
}
|
||||
|
|
|
@ -1275,6 +1275,7 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
}
|
||||
} else {
|
||||
mentionSuggesterToolbar.invisible()
|
||||
mentionSuggesterToolbar.clear()
|
||||
recycler.removeItemDecoration(footerMentionDecorator)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ class ObjectIconWidget @JvmOverloads constructor(
|
|||
is ObjectIcon.Profile.Avatar -> setProfileInitials(icon.name)
|
||||
is ObjectIcon.Profile.Image -> setCircularImage(icon.hash)
|
||||
is ObjectIcon.Task -> setCheckbox(icon.isChecked)
|
||||
ObjectIcon.None -> removeIcon()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,4 +222,10 @@ class ObjectIconWidget @JvmOverloads constructor(
|
|||
rectangularIconContainer.invisible()
|
||||
ivImage.invisible()
|
||||
}
|
||||
|
||||
fun removeIcon() {
|
||||
ivEmoji.setImageDrawable(null)
|
||||
ivImage.setImageDrawable(null)
|
||||
ivCheckbox.invisible()
|
||||
}
|
||||
}
|
|
@ -4,11 +4,9 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.extensions.drawable
|
||||
import com.anytypeio.anytype.core_ui.widgets.toolbar.adapter.MentionAdapter
|
||||
import com.anytypeio.anytype.core_utils.ui.NpaLinearLayoutManager
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import kotlinx.android.synthetic.main.widget_mention_menu.view.*
|
||||
|
||||
|
@ -20,6 +18,17 @@ class MentionToolbar @JvmOverloads constructor(
|
|||
|
||||
private var mentionClick: ((DefaultObjectView, String) -> Unit)? = null
|
||||
private var newPageClick: ((String) -> Unit)? = null
|
||||
private val mentionAdapter by lazy {
|
||||
MentionAdapter(
|
||||
data = arrayListOf(),
|
||||
onClicked = { objectView, filter ->
|
||||
mentionClick?.invoke(objectView, filter)
|
||||
},
|
||||
newClicked = { name ->
|
||||
newPageClick?.invoke(name)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
LayoutInflater
|
||||
|
@ -38,26 +47,22 @@ class MentionToolbar @JvmOverloads constructor(
|
|||
|
||||
private fun setup(context: Context) {
|
||||
with(recyclerView) {
|
||||
val lm = LinearLayoutManager(context)
|
||||
val lm = NpaLinearLayoutManager(context)
|
||||
layoutManager = lm
|
||||
adapter = MentionAdapter(
|
||||
data = arrayListOf(),
|
||||
onClicked = { objectView, filter ->
|
||||
mentionClick?.invoke(objectView, filter)
|
||||
},
|
||||
newClicked = { name ->
|
||||
newPageClick?.invoke(name)
|
||||
}
|
||||
)
|
||||
adapter = mentionAdapter
|
||||
}
|
||||
}
|
||||
|
||||
fun addItems(items: List<DefaultObjectView>) {
|
||||
(recyclerView.adapter as? MentionAdapter)?.setData(items)
|
||||
mentionAdapter.setData(items)
|
||||
}
|
||||
|
||||
fun updateFilter(filter: String) {
|
||||
(recyclerView.adapter as? MentionAdapter)?.updateFilter(filter)
|
||||
mentionAdapter.updateFilter(filter)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
mentionAdapter.clear()
|
||||
}
|
||||
|
||||
fun getMentionSuggesterWidgetMinHeight() = with(context.resources) {
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.filterMentionsBy
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
|
||||
class MentionAdapter(
|
||||
|
@ -16,8 +15,6 @@ class MentionAdapter(
|
|||
private val newClicked: (String) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private val filteredData = mutableListOf<DefaultObjectView>()
|
||||
|
||||
fun setData(mentions: List<DefaultObjectView>) {
|
||||
if (mentions.isEmpty()) {
|
||||
data.clear()
|
||||
|
@ -30,10 +27,13 @@ class MentionAdapter(
|
|||
|
||||
fun updateFilter(filter: String) {
|
||||
mentionFilter = filter
|
||||
filteredData.clear()
|
||||
val filteredMentions = data.filterMentionsBy(text = mentionFilter)
|
||||
filteredData.addAll(filteredMentions)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
mentionFilter = ""
|
||||
val size = data.size
|
||||
data.clear()
|
||||
notifyItemRangeRemoved(0, size)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
|
@ -53,7 +53,7 @@ class MentionAdapter(
|
|||
itemView.setOnClickListener {
|
||||
val pos = bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
onClicked(filteredData[pos - 1], mentionFilter)
|
||||
onClicked(data[pos - 1], mentionFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ class MentionAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = filteredData.size + 1
|
||||
override fun getItemCount(): Int = data.size + 1
|
||||
|
||||
override fun getItemViewType(position: Int): Int = when (position) {
|
||||
POSITION_NEW_PAGE -> TYPE_NEW_PAGE
|
||||
|
@ -70,7 +70,7 @@ class MentionAdapter(
|
|||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is DefaultObjectViewAdapter.ObjectViewHolder) {
|
||||
holder.bind(filteredData[position - 1])
|
||||
holder.bind(data[position - 1])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.anytypeio.anytype.core_utils.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/30220771/recyclerview-inconsistency-detected-invalid-item-position
|
||||
*/
|
||||
class NpaLinearLayoutManager(context: Context?) : LinearLayoutManager(context) {
|
||||
/**
|
||||
* Disable predictive animations. There is a bug in RecyclerView which causes views that
|
||||
* are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
|
||||
* adapter size has decreased since the ViewHolder was recycled.
|
||||
*/
|
||||
override fun supportsPredictiveItemAnimations(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -198,7 +198,7 @@ sealed class ControlPanelMachine {
|
|||
sealed class Mentions : Event() {
|
||||
data class OnStart(val cursorCoordinate: Int, val mentionFrom: Int) : Mentions()
|
||||
data class OnQuery(val text: String) : Mentions()
|
||||
data class OnResult(val mentions: List<DefaultObjectView>) : Mentions()
|
||||
data class OnResult(val mentions: List<DefaultObjectView>, val text: String) : Mentions()
|
||||
object OnMentionClicked : Mentions()
|
||||
object OnStop : Mentions()
|
||||
}
|
||||
|
@ -787,6 +787,7 @@ sealed class ControlPanelMachine {
|
|||
is Event.Mentions.OnResult -> state.copy(
|
||||
mentionToolbar = state.mentionToolbar.copy(
|
||||
mentions = event.mentions,
|
||||
mentionFilter = event.text,
|
||||
updateList = true
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,7 +13,6 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_BOOKMARK
|
|||
import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_DOCUMENT_ICON_MENU
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_DOCUMENT_MENU
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_MARKUP_LINK
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_MENTION_MENU
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_MULTI_SELECT_MENU
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_PROFILE_ICON_MENU
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.POPUP_PROFILE_MENU
|
||||
|
@ -40,13 +39,13 @@ 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.dataview.interactor.GetCompatibleObjectTypes
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.editor.Editor
|
||||
import com.anytypeio.anytype.domain.error.Error
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
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.navigation.GetListPages
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
import com.anytypeio.anytype.presentation.BuildConfig
|
||||
import com.anytypeio.anytype.presentation.common.StateReducer
|
||||
|
@ -64,6 +63,7 @@ import com.anytypeio.anytype.presentation.editor.editor.actions.ActionItemType
|
|||
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ext.*
|
||||
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionConst
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionConst.MENTION_PREFIX
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionConst.MENTION_TITLE_EMPTY
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionEvent
|
||||
|
@ -89,14 +89,17 @@ import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
|
|||
import com.anytypeio.anytype.presentation.editor.search.search
|
||||
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
|
||||
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
|
||||
import com.anytypeio.anytype.presentation.linking.LinkToConstants
|
||||
import com.anytypeio.anytype.presentation.mapper.mark
|
||||
import com.anytypeio.anytype.presentation.mapper.style
|
||||
import com.anytypeio.anytype.presentation.mapper.toMentionView
|
||||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.navigation.SupportNavigation
|
||||
import com.anytypeio.anytype.presentation.objects.SupportedLayouts
|
||||
import com.anytypeio.anytype.presentation.objects.toDefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.relations.DocumentRelationView
|
||||
import com.anytypeio.anytype.presentation.relations.views
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchViewModel
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
|
@ -124,13 +127,13 @@ class EditorViewModel(
|
|||
private val urlBuilder: UrlBuilder,
|
||||
private val renderer: DefaultBlockViewRenderer,
|
||||
private val orchestrator: Orchestrator,
|
||||
private val getListPages: GetListPages,
|
||||
private val analytics: Analytics,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val detailModificationManager: DetailModificationManager,
|
||||
private val updateDetail: UpdateDetail,
|
||||
private val getCompatibleObjectTypes: GetCompatibleObjectTypes,
|
||||
private val objectTypesProvider: ObjectTypesProvider
|
||||
private val objectTypesProvider: ObjectTypesProvider,
|
||||
private val searchObjects: SearchObjects
|
||||
) : ViewStateViewModel<ViewState>(),
|
||||
SupportNavigation<EventWrapper<AppNavigation.Command>>,
|
||||
SupportCommand<Command>,
|
||||
|
@ -199,11 +202,6 @@ class EditorViewModel(
|
|||
*/
|
||||
private var mediaBlockId = ""
|
||||
|
||||
/**
|
||||
* Current position of last mentionFilter or -1 if none
|
||||
*/
|
||||
private var mentionFrom = -1
|
||||
|
||||
/**
|
||||
* Currently pending text update. If null, it is not present or already dispatched.
|
||||
*/
|
||||
|
@ -4922,6 +4920,14 @@ class EditorViewModel(
|
|||
//endregion
|
||||
|
||||
//region MENTION WIDGET
|
||||
/**
|
||||
* Current position of last mentionFilter or -1 if none
|
||||
*/
|
||||
private var mentionFrom = -1
|
||||
private val mentionFilter = MutableStateFlow("")
|
||||
val mentionSearchQuery = mentionFilter.asStateFlow()
|
||||
private var jobMentionFilter: Job? = null
|
||||
|
||||
fun onStartMentionWidgetClicked() {
|
||||
dispatch(Command.AddMentionWidgetTriggerToFocusedBlock)
|
||||
}
|
||||
|
@ -4930,6 +4936,7 @@ class EditorViewModel(
|
|||
Timber.d("onMentionEvent, mentionEvent:[$mentionEvent]")
|
||||
when (mentionEvent) {
|
||||
is MentionEvent.MentionSuggestText -> {
|
||||
mentionFilter.value = mentionEvent.text.toString()
|
||||
controlPanelInteractor.onEvent(
|
||||
ControlPanelMachine.Event.Mentions.OnQuery(
|
||||
text = mentionEvent.text.toString()
|
||||
|
@ -4944,33 +4951,23 @@ class EditorViewModel(
|
|||
mentionFrom = mentionEvent.mentionStart
|
||||
)
|
||||
)
|
||||
viewModelScope.launch {
|
||||
getListPages.invoke(Unit).proceed(
|
||||
failure = { it.timber() },
|
||||
success = { response ->
|
||||
val objectTypes = objectTypesProvider.get()
|
||||
val objectViews = response.listPages.map { pages ->
|
||||
pages.toMentionView(
|
||||
objectTypes = objectTypes,
|
||||
urlBuilder = urlBuilder
|
||||
)
|
||||
}
|
||||
controlPanelInteractor.onEvent(
|
||||
ControlPanelMachine.Event.Mentions.OnResult(objectViews)
|
||||
)
|
||||
}
|
||||
)
|
||||
jobMentionFilter?.cancel()
|
||||
mentionFilter.value = ""
|
||||
jobMentionFilter = viewModelScope.launch {
|
||||
mentionSearchQuery
|
||||
.debounce(300)
|
||||
.collect { onMentionFilter(it) }
|
||||
}
|
||||
viewModelScope.sendEvent(
|
||||
analytics = analytics,
|
||||
eventName = POPUP_MENTION_MENU
|
||||
eventName = EventsDictionary.POPUP_MENTION_MENU
|
||||
)
|
||||
}
|
||||
MentionEvent.MentionSuggestStop -> {
|
||||
mentionFrom = -1
|
||||
controlPanelInteractor.onEvent(
|
||||
ControlPanelMachine.Event.Mentions.OnStop
|
||||
)
|
||||
jobMentionFilter?.cancel()
|
||||
mentionFilter.value = ""
|
||||
controlPanelInteractor.onEvent(ControlPanelMachine.Event.Mentions.OnStop)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5066,5 +5063,43 @@ class EditorViewModel(
|
|||
private fun onMentionClicked(target: String) {
|
||||
proceedWithOpeningObjectByLayout(target)
|
||||
}
|
||||
|
||||
private suspend fun onMentionFilter(filter: String) {
|
||||
controlPanelViewState.value?.let { state ->
|
||||
if (!state.mentionToolbar.isVisible) {
|
||||
jobMentionFilter?.cancel()
|
||||
return
|
||||
}
|
||||
val filters = LinkToConstants.filters
|
||||
val sorts = LinkToConstants.sorts
|
||||
val fullText = filter.removePrefix(MENTION_PREFIX)
|
||||
val params = SearchObjects.Params(
|
||||
limit = ObjectSearchViewModel.SEARCH_LIMIT,
|
||||
filters = filters,
|
||||
sorts = sorts,
|
||||
fulltext = fullText
|
||||
)
|
||||
viewModelScope.launch {
|
||||
searchObjects(params).process(
|
||||
success = { raw ->
|
||||
val objects = raw.toDefaultObjectView(
|
||||
objectTypes = objectTypesProvider.get(),
|
||||
urlBuilder = urlBuilder
|
||||
).filter {
|
||||
SupportedLayouts.layouts.contains(it.typeLayout)
|
||||
&& it.type != ObjectTypeConst.TEMPLATE
|
||||
}
|
||||
controlPanelInteractor.onEvent(
|
||||
ControlPanelMachine.Event.Mentions.OnResult(
|
||||
objects,
|
||||
filter
|
||||
)
|
||||
)
|
||||
},
|
||||
failure = { Timber.e(it, "Error while searching for mention objects") }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
}
|
|
@ -11,11 +11,11 @@ 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.dataview.interactor.GetCompatibleObjectTypes
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
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.navigation.GetListPages
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
import com.anytypeio.anytype.presentation.common.StateReducer
|
||||
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
|
||||
|
@ -39,13 +39,13 @@ open class EditorViewModelFactory(
|
|||
private val urlBuilder: UrlBuilder,
|
||||
private val renderer: DefaultBlockViewRenderer,
|
||||
private val orchestrator: Orchestrator,
|
||||
private val getListPages: GetListPages,
|
||||
private val analytics: Analytics,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val detailModificationManager: DetailModificationManager,
|
||||
private val updateDetail: UpdateDetail,
|
||||
private val getCompatibleObjectTypes: GetCompatibleObjectTypes,
|
||||
private val objectTypesProvider: ObjectTypesProvider
|
||||
private val objectTypesProvider: ObjectTypesProvider,
|
||||
private val searchObjects: SearchObjects
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -66,13 +66,13 @@ open class EditorViewModelFactory(
|
|||
createDocument = createDocument,
|
||||
createNewDocument = createNewDocument,
|
||||
orchestrator = orchestrator,
|
||||
getListPages = getListPages,
|
||||
analytics = analytics,
|
||||
dispatcher = dispatcher,
|
||||
detailModificationManager = detailModificationManager,
|
||||
updateDetail = updateDetail,
|
||||
getCompatibleObjectTypes = getCompatibleObjectTypes,
|
||||
objectTypesProvider = objectTypesProvider
|
||||
objectTypesProvider = objectTypesProvider,
|
||||
searchObjects = searchObjects
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.anytypeio.anytype.presentation.linking
|
||||
|
||||
import com.anytypeio.anytype.core_models.*
|
||||
|
||||
object LinkToConstants {
|
||||
|
||||
val filters = listOf(
|
||||
DVFilter(
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = false,
|
||||
relationKey = Relations.IS_ARCHIVED,
|
||||
operator = DVFilterOperator.AND
|
||||
),
|
||||
DVFilter(
|
||||
relationKey = Relations.IS_HIDDEN,
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = true
|
||||
)
|
||||
)
|
||||
|
||||
val sorts = listOf(
|
||||
DVSort(
|
||||
relationKey = Relations.LAST_OPENED_DATE,
|
||||
type = DVSortType.DESC
|
||||
)
|
||||
)
|
||||
}
|
|
@ -28,27 +28,8 @@ class LinkToObjectViewModel(
|
|||
override fun getSearchObjectsParams(): SearchObjects.Params {
|
||||
|
||||
val filteredTypes = types.value.map { objectType -> objectType.url }
|
||||
|
||||
val filters = listOf(
|
||||
DVFilter(
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = false,
|
||||
relationKey = Relations.IS_ARCHIVED,
|
||||
operator = DVFilterOperator.AND
|
||||
),
|
||||
DVFilter(
|
||||
relationKey = Relations.IS_HIDDEN,
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
value = true
|
||||
)
|
||||
)
|
||||
|
||||
val sorts = listOf(
|
||||
DVSort(
|
||||
relationKey = Relations.LAST_OPENED_DATE,
|
||||
type = DVSortType.DESC
|
||||
)
|
||||
)
|
||||
val filters = LinkToConstants.filters
|
||||
val sorts = LinkToConstants.sorts
|
||||
|
||||
return SearchObjects.Params(
|
||||
limit = SEARCH_LIMIT,
|
||||
|
|
|
@ -8,10 +8,8 @@ import com.anytypeio.anytype.presentation.editor.editor.Markup
|
|||
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.navigation.ObjectView
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.relations.getTypePrettyName
|
||||
import com.anytypeio.anytype.presentation.relations.type
|
||||
import com.anytypeio.anytype.presentation.sets.buildGridRow
|
||||
import com.anytypeio.anytype.presentation.sets.model.*
|
||||
|
@ -447,20 +445,6 @@ fun DocumentInfo.toView(
|
|||
)
|
||||
}
|
||||
|
||||
fun DocumentInfo.toMentionView(
|
||||
urlBuilder: UrlBuilder, objectTypes: List<ObjectType>
|
||||
) = DefaultObjectView(
|
||||
id = id,
|
||||
name = obj.name.orEmpty(),
|
||||
typeName = objectTypes.getTypePrettyName(type = obj.type.firstOrNull()),
|
||||
typeLayout = obj.layout,
|
||||
icon = ObjectIcon.from(
|
||||
obj = obj,
|
||||
layout = obj.layout,
|
||||
builder = urlBuilder
|
||||
)
|
||||
)
|
||||
|
||||
fun Block.Fields.getName(): String =
|
||||
this.name.let { name ->
|
||||
if (name.isNullOrBlank()) Relations.RELATION_NAME_EMPTY else name
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
|||
data class DefaultObjectView(
|
||||
val id: Id,
|
||||
val name: String,
|
||||
val type: String? = null,
|
||||
val typeName: String? = null,
|
||||
val typeLayout: ObjectType.Layout? = null,
|
||||
val icon: ObjectIcon = ObjectIcon.None
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package com.anytypeio.anytype.presentation.objects
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
|
||||
fun List<Map<String, Any?>>.toDefaultObjectView(
|
||||
urlBuilder: UrlBuilder,
|
||||
objectTypes: List<ObjectType>
|
||||
): List<DefaultObjectView> =
|
||||
this.map { record ->
|
||||
val obj = ObjectWrapper.Basic(record)
|
||||
val type = obj.type.firstOrNull()
|
||||
val layout = obj.layout ?: ObjectType.Layout.BASIC
|
||||
DefaultObjectView(
|
||||
id = obj.id,
|
||||
name = obj.name.orEmpty(),
|
||||
typeName = objectTypes.find { it.url == type }?.name.orEmpty(),
|
||||
typeLayout = layout,
|
||||
icon = ObjectIcon.from(
|
||||
obj = obj,
|
||||
layout = layout,
|
||||
builder = urlBuilder
|
||||
),
|
||||
type = type
|
||||
)
|
||||
}
|
|
@ -9,6 +9,7 @@ import com.anytypeio.anytype.presentation.editor.cover.CoverColor
|
|||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
import com.anytypeio.anytype.presentation.extension.isValueRequired
|
||||
import com.anytypeio.anytype.presentation.mapper.*
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSet
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetViewState
|
||||
|
|
|
@ -26,9 +26,11 @@ open class ObjectSearchViewModel(
|
|||
SupportNavigation<EventWrapper<AppNavigation.Command>> {
|
||||
|
||||
private val userInput = MutableStateFlow(EMPTY_QUERY)
|
||||
private val searchQuery = userInput.take(1).onCompletion {
|
||||
emitAll(userInput.debounce(DEBOUNCE_DURATION).distinctUntilChanged())
|
||||
}
|
||||
private val searchQuery = userInput
|
||||
.take(1)
|
||||
.onCompletion {
|
||||
emitAll(userInput.debounce(DEBOUNCE_DURATION).distinctUntilChanged())
|
||||
}
|
||||
|
||||
protected val types = MutableStateFlow(emptyList<ObjectType>())
|
||||
protected val objects = MutableStateFlow(emptyList<ObjectWrapper.Basic>())
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.anytypeio.anytype.domain.clipboard.Copy
|
|||
import com.anytypeio.anytype.domain.clipboard.Paste
|
||||
import com.anytypeio.anytype.domain.config.Gateway
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SetRelationKey
|
||||
import com.anytypeio.anytype.domain.download.DownloadFile
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
|
@ -24,7 +25,6 @@ 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.page.navigation.GetListPages
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
import com.anytypeio.anytype.presentation.MockBlockFactory
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
|
||||
|
@ -130,9 +130,6 @@ open class EditorViewModelTest {
|
|||
@Mock
|
||||
lateinit var splitBlock: SplitBlock
|
||||
|
||||
@Mock
|
||||
lateinit var getListPages: GetListPages
|
||||
|
||||
@Mock
|
||||
lateinit var createPage: CreatePage
|
||||
|
||||
|
@ -211,6 +208,9 @@ open class EditorViewModelTest {
|
|||
@Mock
|
||||
lateinit var setObjectType: SetObjectType
|
||||
|
||||
@Mock
|
||||
lateinit var searchObjects: SearchObjects
|
||||
|
||||
@Mock
|
||||
lateinit var objectTypesProvider: ObjectTypesProvider
|
||||
|
||||
|
@ -3895,7 +3895,6 @@ open class EditorViewModelTest {
|
|||
updateDetail = UpdateDetail(repo)
|
||||
|
||||
vm = EditorViewModel(
|
||||
getListPages = getListPages,
|
||||
openPage = openPage,
|
||||
closePage = closePage,
|
||||
createPage = createPage,
|
||||
|
@ -3959,7 +3958,8 @@ open class EditorViewModelTest {
|
|||
detailModificationManager = InternalDetailModificationManager(storage.details),
|
||||
updateDetail = updateDetail,
|
||||
getCompatibleObjectTypes = getCompatibleObjectTypes,
|
||||
objectTypesProvider = objectTypesProvider
|
||||
objectTypesProvider = objectTypesProvider,
|
||||
searchObjects = searchObjects
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.presentation.editor.editor
|
|||
import MockDataFactory
|
||||
import android.util.Log
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import app.cash.turbine.test
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Event
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
|
@ -12,7 +13,6 @@ import com.anytypeio.anytype.domain.base.Result
|
|||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
import com.anytypeio.anytype.domain.icon.DocumentEmojiIconProvider
|
||||
import com.anytypeio.anytype.domain.page.CreateNewDocument
|
||||
import com.anytypeio.anytype.domain.page.navigation.GetListPages
|
||||
import com.anytypeio.anytype.presentation.editor.EditorViewModel
|
||||
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionConst.MENTION_TITLE_EMPTY
|
||||
|
@ -22,6 +22,8 @@ import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
|||
import com.anytypeio.anytype.presentation.util.TXT
|
||||
import com.jraska.livedata.test
|
||||
import net.lachlanmckee.timberjunit.TimberTestRule
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
@ -30,6 +32,8 @@ import org.mockito.Mockito
|
|||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class EditorMentionTest : EditorPresentationTestSetup() {
|
||||
|
||||
|
@ -133,8 +137,8 @@ class EditorMentionTest : EditorPresentationTestSetup() {
|
|||
onBlocking { invoke(any()) } doReturn Either.Right(Unit)
|
||||
}
|
||||
|
||||
getListPages.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(GetListPages.Response(emptyList()))
|
||||
searchObjects.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(listOf())
|
||||
}
|
||||
|
||||
val vm = buildViewModel()
|
||||
|
@ -285,8 +289,8 @@ class EditorMentionTest : EditorPresentationTestSetup() {
|
|||
onBlocking { invoke(any()) } doReturn Either.Right(Unit)
|
||||
}
|
||||
|
||||
getListPages.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(GetListPages.Response(emptyList()))
|
||||
searchObjects.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(listOf())
|
||||
}
|
||||
|
||||
Mockito.`when`(documentEmojiIconProvider.random()).thenReturn(emoji)
|
||||
|
@ -451,8 +455,8 @@ class EditorMentionTest : EditorPresentationTestSetup() {
|
|||
onBlocking { invoke(any()) } doReturn Either.Right(Unit)
|
||||
}
|
||||
|
||||
getListPages.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(GetListPages.Response(emptyList()))
|
||||
searchObjects.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(listOf())
|
||||
}
|
||||
|
||||
Mockito.`when`(documentEmojiIconProvider.random()).thenReturn(emoji)
|
||||
|
@ -585,8 +589,8 @@ class EditorMentionTest : EditorPresentationTestSetup() {
|
|||
onBlocking { invoke(any()) } doReturn Either.Right(Unit)
|
||||
}
|
||||
|
||||
getListPages.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(GetListPages.Response(emptyList()))
|
||||
searchObjects.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(listOf())
|
||||
}
|
||||
|
||||
val vm = buildViewModel()
|
||||
|
@ -658,6 +662,121 @@ class EditorMentionTest : EditorPresentationTestSetup() {
|
|||
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
|
||||
)
|
||||
)
|
||||
|
||||
clearPendingCoroutines()
|
||||
}
|
||||
|
||||
@ExperimentalTime
|
||||
@Test
|
||||
fun `test mention filters`() {
|
||||
|
||||
val a = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.Text(
|
||||
text = "Start end",
|
||||
marks = listOf(),
|
||||
style = Block.Content.Text.Style.P
|
||||
)
|
||||
)
|
||||
|
||||
val page = Block(
|
||||
id = root,
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.Smart(),
|
||||
children = listOf(a.id)
|
||||
)
|
||||
|
||||
val document = listOf(page, a)
|
||||
|
||||
stubOpenDocument(document)
|
||||
stubInterceptEvents()
|
||||
stubUpdateText()
|
||||
stubSearchObjects()
|
||||
|
||||
val vm = buildViewModel()
|
||||
|
||||
vm.onStart(root)
|
||||
|
||||
//TESTING
|
||||
|
||||
runBlocking {
|
||||
vm.mentionSearchQuery.test {
|
||||
vm.apply {
|
||||
onBlockFocusChanged(
|
||||
id = a.id,
|
||||
hasFocus = true
|
||||
)
|
||||
onSelectionChanged(
|
||||
id = a.id,
|
||||
selection = IntRange(6, 6)
|
||||
)
|
||||
vm.onMentionEvent(
|
||||
MentionEvent.MentionSuggestStart(
|
||||
cursorCoordinate = 999,
|
||||
mentionStart = 6
|
||||
)
|
||||
)
|
||||
vm.onMentionEvent(
|
||||
MentionEvent.MentionSuggestText(text = "@")
|
||||
)
|
||||
vm.onTextBlockTextChanged(
|
||||
view = BlockView.Text.Paragraph(
|
||||
id = a.id,
|
||||
marks = emptyList(),
|
||||
text = "Start @end"
|
||||
)
|
||||
)
|
||||
onSelectionChanged(
|
||||
id = a.id,
|
||||
selection = IntRange(7, 7)
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
expected = "@", actual = expectMostRecentItem()
|
||||
)
|
||||
|
||||
vm.onMentionEvent(
|
||||
MentionEvent.MentionSuggestText(text = "@t")
|
||||
)
|
||||
vm.onTextBlockTextChanged(
|
||||
view = BlockView.Text.Paragraph(
|
||||
id = a.id,
|
||||
marks = emptyList(),
|
||||
text = "Start @tend"
|
||||
)
|
||||
)
|
||||
vm.onSelectionChanged(
|
||||
id = a.id,
|
||||
selection = IntRange(8, 8)
|
||||
)
|
||||
assertEquals(
|
||||
expected = "@t", actual = expectMostRecentItem()
|
||||
)
|
||||
|
||||
vm.onMentionEvent(
|
||||
MentionEvent.MentionSuggestText(text = "@to")
|
||||
)
|
||||
vm.onTextBlockTextChanged(
|
||||
view = BlockView.Text.Paragraph(
|
||||
id = a.id,
|
||||
marks = emptyList(),
|
||||
text = "Start @toend"
|
||||
)
|
||||
)
|
||||
vm.onSelectionChanged(
|
||||
id = a.id,
|
||||
selection = IntRange(9, 9)
|
||||
)
|
||||
assertEquals(
|
||||
expected = "@to", actual = expectMostRecentItem()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
clearPendingCoroutines()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.clipboard.Copy
|
|||
import com.anytypeio.anytype.domain.clipboard.Paste
|
||||
import com.anytypeio.anytype.domain.config.Gateway
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.SetRelationKey
|
||||
import com.anytypeio.anytype.domain.download.DownloadFile
|
||||
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
|
||||
|
@ -23,7 +24,6 @@ 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.page.navigation.GetListPages
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
import com.anytypeio.anytype.presentation.editor.DocumentExternalEventReducer
|
||||
import com.anytypeio.anytype.presentation.editor.Editor
|
||||
|
@ -64,9 +64,6 @@ open class EditorPresentationTestSetup {
|
|||
@Mock
|
||||
lateinit var updateText: UpdateText
|
||||
|
||||
@Mock
|
||||
lateinit var getListPages: GetListPages
|
||||
|
||||
@Mock
|
||||
lateinit var updateCheckbox: UpdateCheckbox
|
||||
|
||||
|
@ -184,6 +181,9 @@ open class EditorPresentationTestSetup {
|
|||
@Mock
|
||||
lateinit var objectTypesProvider: ObjectTypesProvider
|
||||
|
||||
@Mock
|
||||
lateinit var searchObjects: SearchObjects
|
||||
|
||||
private val builder: UrlBuilder get() = UrlBuilder(gateway)
|
||||
|
||||
private lateinit var updateDetail: UpdateDetail
|
||||
|
@ -240,7 +240,6 @@ open class EditorPresentationTestSetup {
|
|||
)
|
||||
|
||||
return EditorViewModel(
|
||||
getListPages = getListPages,
|
||||
openPage = openPage,
|
||||
closePage = closePage,
|
||||
createPage = createPage,
|
||||
|
@ -266,7 +265,8 @@ open class EditorPresentationTestSetup {
|
|||
detailModificationManager = InternalDetailModificationManager(storage.details),
|
||||
updateDetail = updateDetail,
|
||||
getCompatibleObjectTypes = getCompatibleObjectTypes,
|
||||
objectTypesProvider = objectTypesProvider
|
||||
objectTypesProvider = objectTypesProvider,
|
||||
searchObjects = searchObjects
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -508,4 +508,10 @@ open class EditorPresentationTestSetup {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun stubSearchObjects() {
|
||||
searchObjects.stub {
|
||||
onBlocking { invoke(any()) } doReturn Either.Right(listOf())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue