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

Editor | Enhancement | Delete list-block style on backspace press (#2270)

This commit is contained in:
Evgenii Kozlov 2022-05-17 15:19:18 +03:00 committed by GitHub
parent e36b751ebe
commit bd094bda6d
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 333 additions and 120 deletions

View file

@ -1391,25 +1391,38 @@ class EditorViewModel(
Timber.d("onEmptyBlockBackspaceClicked, id:[$id]")
val position = views.indexOfFirst { it.id == id }
if (position > 0) {
val previous = views[position.dec()]
if (previous !is BlockView.Text
&& previous !is BlockView.Title
&& previous !is BlockView.Description
&& previous !is BlockView.FeaturedRelation
) {
val current = views[position]
if (current is BlockView.Text && current.isListBlock) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Unlink(
Intent.Text.UpdateStyle(
context = context,
targets = listOf(previous.id),
previous = null,
next = null,
cursor = null
targets = listOf(id),
style = Content.Text.Style.P
)
)
}
} else {
proceedWithUnlinking(target = id)
val previous = views[position.dec()]
if (previous !is BlockView.Text
&& previous !is BlockView.Title
&& previous !is BlockView.Description
&& previous !is BlockView.FeaturedRelation
) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Unlink(
context = context,
targets = listOf(previous.id),
previous = null,
next = null,
cursor = null
)
)
}
} else {
proceedWithUnlinking(target = id)
}
}
}
}

View file

@ -221,6 +221,8 @@ sealed class BlockView : ViewType {
abstract override var cursor: Int?
abstract override val alignment: Alignment?
val isListBlock: Boolean get() = (this is Bulleted || this is Checkbox || this is Numbered || this is Toggle)
/**
* UI-model for a basic paragraph block.
* @property id block's id

View file

@ -3,8 +3,15 @@ package com.anytypeio.anytype.presentation.editor.editor
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.StubBulleted
import com.anytypeio.anytype.core_models.StubCheckbox
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubNumbered
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.core_models.StubToggle
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.domain.block.interactor.UnlinkBlocks
import com.anytypeio.anytype.domain.block.interactor.UpdateTextStyle
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
@ -17,6 +24,7 @@ import org.junit.Test
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyBlocking
import org.mockito.kotlin.verifyZeroInteractions
class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
@ -26,36 +34,20 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
@get:Rule
val coroutineTestRule = CoroutinesTestRule()
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
}
@Test
fun `should focus parent text block when its child is deleted`() {
fun `should focus parent text block - when its child is deleted`() {
// SETUP
val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = MockDataFactory.randomString(),
style = Block.Content.Text.Style.TITLE,
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
val child = Block(
id = "CHILD",
fields = Block.Fields.empty(),
@ -64,7 +56,13 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
text = "",
marks = emptyList(),
style = Block.Content.Text.Style.values().filter { style ->
style != Block.Content.Text.Style.TITLE && style != Block.Content.Text.Style.DESCRIPTION && style != Block.Content.Text.Style.CALLOUT
style != Block.Content.Text.Style.TITLE
&& style != Block.Content.Text.Style.DESCRIPTION
&& style != Block.Content.Text.Style.CALLOUT
&& style != Block.Content.Text.Style.TOGGLE
&& style != Block.Content.Text.Style.BULLET
&& style != Block.Content.Text.Style.CHECKBOX
&& style != Block.Content.Text.Style.NUMBERED
}.random()
)
)
@ -148,30 +146,10 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
}
@Test
fun `should focus previous nested text block when the next one is deleted`() {
fun `should focus previous nested text block - when the next one is deleted`() {
// SETUP
val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = MockDataFactory.randomString(),
style = Block.Content.Text.Style.TITLE,
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
val child1 = Block(
id = "CHILD1",
fields = Block.Fields.empty(),
@ -191,7 +169,14 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
text = "",
marks = emptyList(),
style = Block.Content.Text.Style.values().filter { style ->
style != Block.Content.Text.Style.TITLE && style != Block.Content.Text.Style.DESCRIPTION && style != Block.Content.Text.Style.CALLOUT
style != Block.Content.Text.Style.TITLE
&& style != Block.Content.Text.Style.DESCRIPTION
&& style != Block.Content.Text.Style.CALLOUT
&& style != Block.Content.Text.Style.TOGGLE
&& style != Block.Content.Text.Style.BULLET
&& style != Block.Content.Text.Style.CHECKBOX
&& style != Block.Content.Text.Style.NUMBERED
}.random()
)
)
@ -281,30 +266,10 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
}
@Test
fun `should focus the previous nested textual block when the next text block is deleted`() {
fun `should focus the previous nested textual block - when the next text block is deleted`() {
// SETUP
val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = MockDataFactory.randomString(),
style = Block.Content.Text.Style.TITLE,
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
val child1 = Block(
id = "CHILD1-TEXT",
fields = Block.Fields.empty(),
@ -324,7 +289,13 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
text = MockDataFactory.randomString(),
marks = emptyList(),
style = Block.Content.Text.Style.values().filter { style ->
style != Block.Content.Text.Style.TITLE && style != Block.Content.Text.Style.DESCRIPTION && style != Block.Content.Text.Style.CALLOUT
style != Block.Content.Text.Style.TITLE
&& style != Block.Content.Text.Style.DESCRIPTION
&& style != Block.Content.Text.Style.CALLOUT
&& style != Block.Content.Text.Style.TOGGLE
&& style != Block.Content.Text.Style.BULLET
&& style != Block.Content.Text.Style.CHECKBOX
&& style != Block.Content.Text.Style.NUMBERED
}.random()
)
)
@ -414,30 +385,10 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
}
@Test
fun `should delete the previous bookmark when pressing backspace in an empty text block following this bookmark`() {
fun `should delete the previous bookmark - when pressing backspace in an empty text block following this bookmark`() {
// SETUP
val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = MockDataFactory.randomString(),
style = Block.Content.Text.Style.TITLE,
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
val bookmark = Block(
id = MockDataFactory.randomString(),
fields = Block.Fields.empty(),
@ -538,30 +489,10 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
}
@Test
fun `should delete the previous bookmark when pressing backspace in an non-empty text block following this bookmark`() {
fun `should delete the previous bookmark - when pressing backspace in an non-empty text block following this bookmark`() {
// SETUP
val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = MockDataFactory.randomString(),
style = Block.Content.Text.Style.TITLE,
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
val header = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(title.id)
)
val bookmark = Block(
id = MockDataFactory.randomString(),
fields = Block.Fields.empty(),
@ -665,4 +596,180 @@ class EditorBackspaceDeleteTest : EditorPresentationTestSetup() {
)
}
}
@Test
fun `should reset style to paragraph - when pressing backspace in empty checkbox block`() {
// SETUP
val checkbox = StubCheckbox()
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(),
children = listOf(header.id, checkbox.id)
)
val document = listOf(page, header, title, checkbox)
stubOpenDocument(document = document)
stubUpdateTextStyle()
val vm = buildViewModel()
// TESTING
vm.apply {
onStart(root)
onBlockFocusChanged(
id = checkbox.id,
hasFocus = true
)
onEmptyBlockBackspaceClicked(id = checkbox.id)
}
verifyZeroInteractions(unlinkBlocks)
verifyBlocking(updateTextStyle, times(1)) {
invoke(
UpdateTextStyle.Params(
context = root,
targets = listOf(checkbox.id),
style = Block.Content.Text.Style.P
)
)
}
}
@Test
fun `should reset style to paragraph - when pressing backspace in empty bulleted block`() {
// SETUP
val bulleted = StubBulleted()
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(),
children = listOf(header.id, bulleted.id)
)
val document = listOf(page, header, title, bulleted)
stubOpenDocument(document = document)
stubUpdateTextStyle()
val vm = buildViewModel()
// TESTING
vm.apply {
onStart(root)
onBlockFocusChanged(
id = bulleted.id,
hasFocus = true
)
onEmptyBlockBackspaceClicked(id = bulleted.id)
}
verifyZeroInteractions(unlinkBlocks)
verifyBlocking(updateTextStyle, times(1)) {
invoke(
UpdateTextStyle.Params(
context = root,
targets = listOf(bulleted.id),
style = Block.Content.Text.Style.P
)
)
}
}
@Test
fun `should reset style to paragraph - when pressing backspace in empty numbered block`() {
// SETUP
val numbered = StubNumbered()
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(),
children = listOf(header.id, numbered.id)
)
val document = listOf(page, header, title, numbered)
stubOpenDocument(document = document)
stubUpdateTextStyle()
val vm = buildViewModel()
// TESTING
vm.apply {
onStart(root)
onBlockFocusChanged(
id = numbered.id,
hasFocus = true
)
onEmptyBlockBackspaceClicked(id = numbered.id)
}
verifyZeroInteractions(unlinkBlocks)
verifyBlocking(updateTextStyle, times(1)) {
invoke(
UpdateTextStyle.Params(
context = root,
targets = listOf(numbered.id),
style = Block.Content.Text.Style.P
)
)
}
}
@Test
fun `should reset style to paragraph - when pressing backspace in empty toggle block`() {
// SETUP
val toggle = StubToggle()
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(),
children = listOf(header.id, toggle.id)
)
val document = listOf(page, header, title, toggle)
stubOpenDocument(document = document)
stubUpdateTextStyle()
val vm = buildViewModel()
// TESTING
vm.apply {
onStart(root)
onBlockFocusChanged(
id = toggle.id,
hasFocus = true
)
onEmptyBlockBackspaceClicked(id = toggle.id)
}
verifyZeroInteractions(unlinkBlocks)
verifyBlocking(updateTextStyle, times(1)) {
invoke(
UpdateTextStyle.Params(
context = root,
targets = listOf(toggle.id),
style = Block.Content.Text.Style.P
)
)
}
}
}

View file

@ -0,0 +1,91 @@
package com.anytypeio.anytype.core_models
import com.anytypeio.anytype.test_utils.MockDataFactory
fun StubHeader(
children: List<Id> = emptyList()
): Block = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = children
)
fun StubTitle(
text: String = MockDataFactory.randomString()
): Block = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = text,
style = Block.Content.Text.Style.TITLE,
marks = emptyList()
),
children = emptyList(),
fields = Block.Fields.empty()
)
fun StubCheckbox(
text: String = MockDataFactory.randomString(),
children: List<Id> = emptyList(),
marks: List<Block.Content.Text.Mark> = emptyList(),
isChecked: Boolean = MockDataFactory.randomBoolean()
): Block = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = text,
style = Block.Content.Text.Style.CHECKBOX,
marks = marks,
isChecked = isChecked
),
children = children,
fields = Block.Fields.empty()
)
fun StubBulleted(
text: String = MockDataFactory.randomString(),
children: List<Id> = emptyList(),
marks: List<Block.Content.Text.Mark> = emptyList(),
isChecked: Boolean = MockDataFactory.randomBoolean()
): Block = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = text,
style = Block.Content.Text.Style.BULLET,
marks = marks,
isChecked = isChecked
),
children = children,
fields = Block.Fields.empty()
)
fun StubToggle(
text: String = MockDataFactory.randomString(),
children: List<Id> = emptyList(),
marks: List<Block.Content.Text.Mark> = emptyList()
): Block = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = text,
style = Block.Content.Text.Style.TOGGLE,
marks = marks
),
children = children,
fields = Block.Fields.empty()
)
fun StubNumbered(
text: String = MockDataFactory.randomString(),
children: List<Id> = emptyList(),
marks: List<Block.Content.Text.Mark> = emptyList()
): Block = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
text = text,
style = Block.Content.Text.Style.NUMBERED,
marks = marks
),
children = children,
fields = Block.Fields.empty()
)