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

Editor | Show description block based on featured relations (#1414)

This commit is contained in:
Evgenii Kozlov 2021-04-15 22:31:56 +03:00 committed by GitHub
parent 84d253ce04
commit af86afbeda
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 255 additions and 28 deletions

View file

@ -4,7 +4,7 @@
### Middleware ⚙
* Updated middleware protocol to `0.15.0-rc19`
* Updated middleware protocol to `0.15.0-rc21`
## Version 0.1.7

View file

@ -9,14 +9,13 @@ import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.domain.relations.Relations
import com.anytypeio.anytype.emojifier.data.DefaultDocumentEmojiIconProvider
import com.anytypeio.anytype.features.editor.base.EditorTestSetup
import com.anytypeio.anytype.features.editor.base.TestPageFragment
import com.anytypeio.anytype.mocking.MockDataFactory
import com.anytypeio.anytype.ui.page.PageFragment
import com.anytypeio.anytype.utils.CoroutinesTestRule
import com.anytypeio.anytype.utils.checkHasText
import com.anytypeio.anytype.utils.onItemView
import com.anytypeio.anytype.utils.rVMatcher
import com.anytypeio.anytype.utils.*
import com.bartoszlipinski.disableanimationsrule.DisableAnimationsRule
import org.junit.Before
import org.junit.Rule
@ -52,7 +51,7 @@ class DescriptionTesting : EditorTestSetup() {
}
@Test
fun shouldRenderDescriptionAfterTitle() {
fun shouldNotRenderDescriptionAfterTitleBecauseDescriptionIsNotFeatured() {
// SETUP
@ -95,6 +94,73 @@ class DescriptionTesting : EditorTestSetup() {
// TESTING
R.id.recycler.rVMatcher().apply {
onItemView(0, R.id.title).checkHasText(
title.content<Block.Content.Text>().text
)
checkIsRecyclerSize(1)
}
}
@Test
fun shouldRenderDescriptionAfterTitleBecauseDescriptionIsFeatured() {
// SETUP
val description = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = "A lighthouse is a tower, building, or another type of structure designed to emit light from a system of lamps and lenses and to serve as a navigational aid for maritime pilots at sea or on inland waterways.",
marks = emptyList(),
style = Block.Content.Text.Style.DESCRIPTION
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id, description.id)
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(
type = Block.Content.Smart.Type.PAGE
),
children = listOf(header.id)
)
val document = listOf(page, header, title, description)
val details = Block.Details(
mapOf(
root to Block.Fields(
mapOf(
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
"featuredRelations" to listOf(Relations.DESCRIPTION),
"description" to description.content<Block.Content.Text>().text
)
)
)
)
stubInterceptEvents()
stubInterceptThreadStatus()
stubOpenDocument(
document = document,
details = details
)
launchFragment(args)
// TESTING
R.id.recycler.rVMatcher().apply {
onItemView(0, R.id.title).checkHasText(
title.content<Block.Content.Text>().text
@ -102,6 +168,7 @@ class DescriptionTesting : EditorTestSetup() {
onItemView(1, R.id.tvBlockDescription).checkHasText(
description.content<Block.Content.Text>().text
)
checkIsRecyclerSize(2)
}
}

View file

@ -9,6 +9,7 @@ import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.domain.relations.Relations
import com.anytypeio.anytype.emojifier.data.DefaultDocumentEmojiIconProvider
import com.anytypeio.anytype.features.editor.base.EditorTestSetup
import com.anytypeio.anytype.features.editor.base.TestPageFragment
@ -172,6 +173,117 @@ class FeaturedRelationTesting : EditorTestSetup() {
}
}
@Test
fun shouldRenderTwoPrimitiveFeaturedRelationsAndExcludeDescriptionRelation() {
val relation1 = Relation(
key = Relations.DESCRIPTION,
name = "Company",
format = Relation.Format.SHORT_TEXT,
source = Relation.Source.values().random()
)
val relation2 = Relation(
key = MockDataFactory.randomString(),
name = "Year",
format = Relation.Format.NUMBER,
source = Relation.Source.values().random()
)
val relation3 = Relation(
key = MockDataFactory.randomString(),
name = "Phone",
format = Relation.Format.PHONE,
source = Relation.Source.values().random()
)
val relation4 = Relation(
key = MockDataFactory.randomString(),
name = "Website",
format = Relation.Format.URL,
source = Relation.Source.values().random()
)
val relation5 = Relation(
key = MockDataFactory.randomString(),
name = "Email",
format = Relation.Format.EMAIL,
source = Relation.Source.values().random()
)
val value1 = "Anytype"
val value2 = "2021"
val value3 = "+00000000000"
val value4 = "https://anytype.io/"
val value5 = "team@anytype.io"
val customDetails = Block.Details(
mapOf(
root to Block.Fields(
mapOf(
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
relation1.key to value1,
relation2.key to value2,
relation3.key to value3,
relation4.key to value4,
relation5.key to value5,
"featuredRelations" to listOf(
relation1.key,
relation4.key,
relation5.key
)
)
)
)
)
val paragraph = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Text(
text = "Foo",
marks = emptyList(),
style = Block.Content.Text.Style.P
)
)
val block1 = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(
type = Block.Content.Smart.Type.PAGE
),
children = listOf(header.id, paragraph.id, block1.id)
)
val document = listOf(page, header, title, paragraph, block1)
stubInterceptEvents()
stubInterceptThreadStatus()
stubOpenDocument(
document = document,
details = customDetails,
relations = listOf(relation1, relation2, relation3, relation4, relation5)
)
// TESTING
launchFragment(args)
R.id.featuredRelationRoot.matchView().apply {
checkHasViewGroupChildWithText(1, value4)
checkHasViewGroupChildWithText(3, value5)
}
}
private fun launchFragment(args: Bundle): FragmentScenario<TestPageFragment> {
return launchFragmentInContainer(
fragmentArgs = args,

View file

@ -42,6 +42,7 @@ data class Block(
fun empty(): Fields = Fields(emptyMap())
const val NAME_KEY = "name"
const val TYPE_KEY = "type"
const val DESCRIPTION_KEY = "description"
const val FEATURED_RELATIONS_KEY = "featuredRelations"
const val IS_ARCHIVED_KEY = "isArchived"
}

View file

@ -0,0 +1,8 @@
package com.anytypeio.anytype.domain.relations
/**
* Keys for predefined, bundled relations.
*/
object Relations {
const val DESCRIPTION = "description"
}

View file

@ -2095,7 +2095,9 @@ class PageViewModel(
private fun onSelectAllClicked(state: ViewState.Success) =
state.blocks.map { block ->
if (block.id != blocks.titleId()) select(block.id)
if (block is BlockView.Selectable) {
select(block.id)
}
block.updateSelection(newSelection = true)
}.let {
onMultiSelectModeBlockClicked()

View file

@ -8,6 +8,7 @@ import com.anytypeio.anytype.domain.editor.Editor.Cursor
import com.anytypeio.anytype.domain.editor.Editor.Focus
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.page.EditorMode
import com.anytypeio.anytype.domain.relations.Relations
import com.anytypeio.anytype.presentation.mapper.*
import com.anytypeio.anytype.presentation.page.cover.CoverColor
import com.anytypeio.anytype.presentation.page.cover.CoverImageHashProvider
@ -281,14 +282,18 @@ class DefaultBlockViewRenderer(
}
}
Content.Text.Style.DESCRIPTION -> {
counter.reset()
result.add(
description(
block = block,
content = content,
mode = mode
val detail = details.details.getOrDefault(root.id, Block.Fields.empty())
val featured = detail.featuredRelations ?: emptyList()
if (featured.contains(Relations.DESCRIPTION)) {
counter.reset()
result.add(
description(
block = block,
content = content,
mode = mode
)
)
)
}
}
Content.Text.Style.CHECKBOX -> {
counter.reset()
@ -935,12 +940,16 @@ class DefaultBlockViewRenderer(
return BlockView.FeaturedRelation(
id = block.id,
relations = featured.mapNotNull { id ->
val relation = relations.first { it.key == id }
relation.view(
details = details,
values = details.details[ctx]?.map ?: emptyMap(),
urlBuilder = urlBuilder
)
if (id != Relations.DESCRIPTION) {
val relation = relations.first { it.key == id }
relation.view(
details = details,
values = details.details[ctx]?.map ?: emptyMap(),
urlBuilder = urlBuilder
)
} else {
null
}
}
)
}

View file

@ -2,11 +2,11 @@ package com.anytypeio.anytype.presentation.page.editor
import MockDataFactory
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.anytypeio.anytype.domain.block.interactor.UnlinkBlocks
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.domain.block.interactor.UnlinkBlocks
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.presentation.page.editor.model.BlockView
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.jraska.livedata.test
@ -60,7 +60,9 @@ class EditorBackspaceNestedDeleteTest : EditorPresentationTestSetup() {
content = Block.Content.Text(
text = "",
marks = emptyList(),
style = Block.Content.Text.Style.values().random()
style = Block.Content.Text.Style.values().filter { style ->
style != Block.Content.Text.Style.TITLE && style != Block.Content.Text.Style.DESCRIPTION
}.random()
)
)
@ -187,7 +189,9 @@ class EditorBackspaceNestedDeleteTest : EditorPresentationTestSetup() {
content = Block.Content.Text(
text = "",
marks = emptyList(),
style = Block.Content.Text.Style.values().random()
style = Block.Content.Text.Style.values().filter { style ->
style != Block.Content.Text.Style.TITLE && style != Block.Content.Text.Style.DESCRIPTION
}.random()
)
)

View file

@ -21,6 +21,26 @@ class EditorFocusTest : EditorPresentationTestSetup() {
@get:Rule
val coroutineTestRule = CoroutinesTestRule()
private val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
style = Block.Content.Text.Style.TITLE,
text = "Relation Block UI Testing",
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
private val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@ -47,8 +67,10 @@ class EditorFocusTest : EditorPresentationTestSetup() {
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(Block.Content.Smart.Type.PAGE),
children = listOf(block.id)
children = listOf(header.id, block.id)
),
header,
title,
block
)
@ -111,11 +133,13 @@ class EditorFocusTest : EditorPresentationTestSetup() {
Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Page(
style = Block.Content.Page.Style.SET
content = Block.Content.Smart(
type = Block.Content.Smart.Type.PAGE
),
children = listOf(block.id)
children = listOf(header.id, block.id)
),
header,
title,
block
)