mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-10 10:00:44 +09:00
App | Feature | Object types sorting (#1910)
* add weight to object types views * add task url * fixes * fix * fix * remove nullable
This commit is contained in:
parent
db125cf040
commit
a02b9feb04
7 changed files with 426 additions and 14 deletions
|
@ -59,9 +59,36 @@ data class ObjectType(
|
|||
const val VIDEO_URL = "_otvideo"
|
||||
const val AUDIO_URL = "_otaudio"
|
||||
const val SET_URL = "_otset"
|
||||
const val TASK_URL = "_ottask"
|
||||
const val DATE_URL = "_otdate"
|
||||
const val PROFILE_URL = "_otprofile" //contains User Profile page and Anytype Person page
|
||||
const val NOTE_URL = "_otnote"
|
||||
const val WORKSPACE_URL = "_otspace"
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectTypeComparator : Comparator<ObjectType> {
|
||||
|
||||
override fun compare(o1: ObjectType, o2: ObjectType): Int {
|
||||
val o1Url = o1.url
|
||||
val o2Url = o2.url
|
||||
if (o1Url == o2Url) return 0
|
||||
|
||||
if (o1Url == ObjectType.PAGE_URL && o2Url != ObjectType.PAGE_URL) return -1
|
||||
if (o1Url != ObjectType.PAGE_URL && o2Url == ObjectType.PAGE_URL) return 1
|
||||
|
||||
if (o1Url == ObjectType.NOTE_URL && o2Url != ObjectType.NOTE_URL) return -1
|
||||
if (o1Url != ObjectType.NOTE_URL && o2Url == ObjectType.NOTE_URL) return 1
|
||||
|
||||
if (o1Url == ObjectType.SET_URL && o2Url != ObjectType.SET_URL) return -1
|
||||
if (o1Url != ObjectType.SET_URL && o2Url == ObjectType.SET_URL) return 1
|
||||
|
||||
if (o1Url == ObjectType.TASK_URL && o2Url != ObjectType.TASK_URL) return -1
|
||||
if (o1Url != ObjectType.TASK_URL && o2Url == ObjectType.TASK_URL) return 1
|
||||
|
||||
val o1Name = o1.name
|
||||
val o2Name = o2.name
|
||||
|
||||
return o1Name.compareTo(o2Name)
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ class ViewerGridAdapter(
|
|||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): RecordHolder {
|
||||
Timber.d("OnCreateViewHolder")
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view = inflater.inflate(R.layout.item_viewer_grid_row, parent, false)
|
||||
view.rowCellRecycler.apply {
|
||||
|
@ -48,7 +47,6 @@ class ViewerGridAdapter(
|
|||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecordHolder, position: Int) {
|
||||
Timber.d("Binding record holder")
|
||||
holder.bindObjectHeader(getItem(position))
|
||||
holder.bindObjectCells(getItem(position))
|
||||
}
|
||||
|
@ -81,7 +79,6 @@ class ViewerGridAdapter(
|
|||
val adapter get() = itemView.rowCellRecycler.adapter as ViewerGridCellsAdapter
|
||||
|
||||
fun bindObjectHeader(row: Viewer.GridView.Row) {
|
||||
Timber.d("Binding object header")
|
||||
when (row.layout) {
|
||||
ObjectType.Layout.TODO -> {
|
||||
itemView.objectIcon.visible()
|
||||
|
@ -115,7 +112,6 @@ class ViewerGridAdapter(
|
|||
}
|
||||
|
||||
fun bindObjectCells(row: Viewer.GridView.Row) {
|
||||
Timber.d("Binding object cells")
|
||||
adapter.update(row.cells)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,14 +17,12 @@ class ViewerGridCellsAdapter(
|
|||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
fun update(update: List<CellView>) {
|
||||
Timber.d("Updating cells: update size - ${update.size}, current - ${cells.size}")
|
||||
val diff = DiffUtil.calculateDiff(CellViewDiffUtil(old = cells, new = update), false)
|
||||
cells = update
|
||||
diff.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
Timber.d("onCreateViewHolder")
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
HOLDER_DESCRIPTION -> {
|
||||
|
@ -206,7 +204,6 @@ class ViewerGridCellsAdapter(
|
|||
override fun getItemCount(): Int = cells.size
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
Timber.d("onBindViewHolder")
|
||||
when (holder) {
|
||||
is DVGridCellDescriptionHolder -> holder.bind(cells[position] as CellView.Description)
|
||||
is DVGridCellDateHolder -> holder.bind(cells[position] as CellView.Date)
|
||||
|
|
|
@ -37,7 +37,6 @@ class ViewerGridHeaderAdapter() :
|
|||
sealed class HeaderViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
class DefaultHolder(view: View) : HeaderViewHolder(view) {
|
||||
fun bind(item: ColumnView) {
|
||||
Timber.d("Binding default holder")
|
||||
itemView.cellText.text = item.text
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.domain.dataview.interactor
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeComparator
|
||||
import com.anytypeio.anytype.core_models.SmartBlockType
|
||||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
|
@ -13,9 +14,9 @@ class GetCompatibleObjectTypes(
|
|||
) : BaseUseCase<List<ObjectType>, GetCompatibleObjectTypes.Params>() {
|
||||
|
||||
override suspend fun run(params: Params) = safe {
|
||||
repo.getObjectTypes().filter { oType ->
|
||||
oType.smartBlockTypes.contains(params.smartBlockType) && !oType.isArchived
|
||||
}
|
||||
repo.getObjectTypes()
|
||||
.filter { it.smartBlockTypes.contains(params.smartBlockType) && !it.isArchived }
|
||||
.sortedWith(ObjectTypeComparator())
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,392 @@
|
|||
package com.anytypeio.anytype.domain.block.interactor
|
||||
|
||||
import com.anytypeio.anytype.core_models.CoroutineTestRule
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.SmartBlockType
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.common.MockDataFactory
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
|
||||
import com.nhaarman.mockitokotlin2.doReturn
|
||||
import com.nhaarman.mockitokotlin2.stub
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class GetCompatibleObjectTypesTest {
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@get:Rule
|
||||
var rule = CoroutineTestRule()
|
||||
|
||||
@Mock
|
||||
lateinit var repository: BlockRepository
|
||||
|
||||
lateinit var getCompatibleObjectTypes: GetCompatibleObjectTypes
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
getCompatibleObjectTypes = GetCompatibleObjectTypes(repository)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFilterResultBySmartBlockType() {
|
||||
|
||||
val type1 = ObjectType(
|
||||
url = ObjectType.PAGE_URL,
|
||||
name = "AAA",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type2 = ObjectType(
|
||||
url = ObjectType.PAGE_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.WORKSPACE),
|
||||
isArchived = true,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type3 = ObjectType(
|
||||
url = ObjectType.PAGE_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.DATE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type4 = ObjectType(
|
||||
url = ObjectType.PAGE_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.SET),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type5 = ObjectType(
|
||||
url = ObjectType.PAGE_URL,
|
||||
name = "ZZZ",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.ARCHIVE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
repository.stub {
|
||||
onBlocking { getObjectTypes() } doReturn listOf(type1, type2, type3, type4, type5)
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
|
||||
val params = GetCompatibleObjectTypes.Params(smartBlockType = SmartBlockType.PAGE)
|
||||
|
||||
val expected = listOf(type1, type5)
|
||||
getCompatibleObjectTypes.run(params).process(
|
||||
failure = {},
|
||||
success = {
|
||||
assertEquals(expected, it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSortResultByUrl() {
|
||||
|
||||
val type1 = ObjectType(
|
||||
url = ObjectType.TEMPLATE_URL,
|
||||
name = "QName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type2 = ObjectType(
|
||||
url = ObjectType.SET_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type3 = ObjectType(
|
||||
url = ObjectType.PAGE_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type4 = ObjectType(
|
||||
url = ObjectType.TASK_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type5 = ObjectType(
|
||||
url = ObjectType.NOTE_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.ARCHIVE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type6 = ObjectType(
|
||||
url = ObjectType.AUDIO_URL,
|
||||
name = "NName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.ARCHIVE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type7 = ObjectType(
|
||||
url = ObjectType.RELATION_URL,
|
||||
name = "XName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.ANYTYPE_PROFILE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type8 = ObjectType(
|
||||
url = "_customType",
|
||||
name = "AName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.CUSTOM_OBJECT_TYPE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
repository.stub {
|
||||
onBlocking { getObjectTypes() } doReturn listOf(
|
||||
type1,
|
||||
type2,
|
||||
type3,
|
||||
type4,
|
||||
type5,
|
||||
type6,
|
||||
type7,
|
||||
type8
|
||||
)
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
|
||||
val params = GetCompatibleObjectTypes.Params(smartBlockType = SmartBlockType.PAGE)
|
||||
|
||||
val expected = listOf(type3, type5, type2, type4, type8, type6, type1, type7)
|
||||
|
||||
getCompatibleObjectTypes.run(params).process(
|
||||
failure = {},
|
||||
success = {
|
||||
assertEquals(expected, it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSortResultByTypeUrlAndArchived() {
|
||||
|
||||
val type1 = ObjectType(
|
||||
url = ObjectType.TEMPLATE_URL,
|
||||
name = "QName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = true,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type2 = ObjectType(
|
||||
url = ObjectType.SET_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type3 = ObjectType(
|
||||
url = ObjectType.PAGE_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type4 = ObjectType(
|
||||
url = ObjectType.TASK_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.PAGE),
|
||||
isArchived = true,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type5 = ObjectType(
|
||||
url = ObjectType.NOTE_URL,
|
||||
name = MockDataFactory.randomString(),
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.ARCHIVE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type6 = ObjectType(
|
||||
url = ObjectType.AUDIO_URL,
|
||||
name = "NName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.ARCHIVE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type7 = ObjectType(
|
||||
url = ObjectType.RELATION_URL,
|
||||
name = "XName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.ANYTYPE_PROFILE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
val type8 = ObjectType(
|
||||
url = "_customType",
|
||||
name = "AName",
|
||||
relations = emptyList(),
|
||||
layout = ObjectType.Layout.values().random(),
|
||||
emoji = MockDataFactory.randomString(),
|
||||
description = MockDataFactory.randomString(),
|
||||
isHidden = MockDataFactory.randomBoolean(),
|
||||
smartBlockTypes = listOf(SmartBlockType.CUSTOM_OBJECT_TYPE, SmartBlockType.PAGE),
|
||||
isArchived = false,
|
||||
isReadOnly = false
|
||||
)
|
||||
|
||||
repository.stub {
|
||||
onBlocking { getObjectTypes() } doReturn listOf(
|
||||
type1,
|
||||
type2,
|
||||
type3,
|
||||
type4,
|
||||
type5,
|
||||
type6,
|
||||
type7,
|
||||
type8
|
||||
)
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
|
||||
val params = GetCompatibleObjectTypes.Params(smartBlockType = SmartBlockType.PAGE)
|
||||
|
||||
val expected = listOf(type3, type5, type2, type8, type6)
|
||||
|
||||
getCompatibleObjectTypes.run(params).process(
|
||||
failure = {},
|
||||
success = {
|
||||
assertEquals(expected, it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5265,10 +5265,10 @@ class EditorViewModel(
|
|||
getDefaultEditorType.invoke(Unit).proceed(
|
||||
failure = { Timber.e(it, "Error while getting default object type") },
|
||||
success = { response ->
|
||||
val sorted = listOf(ObjectTypeView.Search) + views.toMutableList()
|
||||
.sortByType(response.type)
|
||||
val filtered = views.filter { it.id != response.type }
|
||||
val result = listOf(ObjectTypeView.Search) + filtered
|
||||
controlPanelInteractor.onEvent(
|
||||
ControlPanelMachine.Event.ObjectTypesWidgetEvent.Show(sorted)
|
||||
ControlPanelMachine.Event.ObjectTypesWidgetEvent.Show(result)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue