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

Should replace current text block instead of adding new block after this text block if this text block is empty when adding new block via add-block screen (#790)

This commit is contained in:
Evgenii Kozlov 2020-09-04 00:01:24 +03:00 committed by GitHub
parent e5dd935fa4
commit 16bc727318
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 295 additions and 43 deletions

View file

@ -8,6 +8,7 @@
### Fixes & tech 🚒
* When adding new block via add-block screen, should replace current text block instead of adding a new block after this text block if this text block is empty (#325)
* Divider block should be selectable in multi-select and scroll-and-move mode (#778)
## Version 0.0.45

View file

@ -54,6 +54,7 @@ import com.agileburo.anytype.core_utils.common.EventWrapper
import com.agileburo.anytype.core_utils.ext.*
import com.agileburo.anytype.core_utils.ext.PopupExtensions.calculateRectInWindow
import com.agileburo.anytype.di.common.componentManager
import com.agileburo.anytype.domain.block.model.Block
import com.agileburo.anytype.domain.block.model.Block.Content.Text
import com.agileburo.anytype.domain.common.Id
import com.agileburo.anytype.domain.ext.getFirstLinkMarkupParam
@ -505,9 +506,9 @@ open class PageFragment :
UiBlock.TOGGLE -> vm.onAddTextBlockClicked(Text.Style.TOGGLE)
UiBlock.CODE -> vm.onAddTextBlockClicked(Text.Style.CODE_SNIPPET)
UiBlock.PAGE -> vm.onAddNewPageClicked()
UiBlock.FILE -> vm.onAddFileBlockClicked()
UiBlock.IMAGE -> vm.onAddImageBlockClicked()
UiBlock.VIDEO -> vm.onAddVideoBlockClicked()
UiBlock.FILE -> vm.onAddFileBlockClicked(Block.Content.File.Type.FILE)
UiBlock.IMAGE -> vm.onAddFileBlockClicked(Block.Content.File.Type.IMAGE)
UiBlock.VIDEO -> vm.onAddFileBlockClicked(Block.Content.File.Type.VIDEO)
UiBlock.BOOKMARK -> vm.onAddBookmarkBlockClicked()
UiBlock.LINE_DIVIDER -> vm.onAddDividerBlockClicked()
else -> toast(NOT_IMPLEMENTED_MESSAGE)

View file

@ -1168,18 +1168,29 @@ class PageViewModel(
}
fun onAddTextBlockClicked(style: Content.Text.Style) {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
proceedWithCreatingNewTextBlock(
id = orchestrator.stores.focus.current().id,
style = style
)
}
fun onAddVideoBlockClicked() {
proceedWithCreatingEmptyFileBlock(
id = orchestrator.stores.focus.current().id,
type = Content.File.Type.VIDEO
)
val target = blocks.first { it.id == orchestrator.stores.focus.current().id }
val content = target.content
if (content is Content.Text && content.text.isEmpty()) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Replace(
context = context,
target = target.id,
prototype = Prototype.Text(style = style)
)
)
}
} else {
proceedWithCreatingNewTextBlock(
id = orchestrator.stores.focus.current().id,
style = style
)
}
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
}
private fun onAddLocalVideoClicked(blockId: String) {
@ -1217,18 +1228,21 @@ class PageViewModel(
dispatch(Command.OpenGallery(mediaType = MIME_FILE_ALL))
}
fun onAddImageBlockClicked() {
proceedWithCreatingEmptyFileBlock(
id = orchestrator.stores.focus.current().id,
type = Content.File.Type.IMAGE
)
}
fun onAddFileBlockClicked(type: Content.File.Type) {
val target = blocks.first { it.id == orchestrator.stores.focus.current().id }
val content = target.content
fun onAddFileBlockClicked() {
proceedWithCreatingEmptyFileBlock(
id = orchestrator.stores.focus.current().id,
type = Content.File.Type.FILE
)
if (content is Content.Text && content.text.isEmpty()) {
proceedWithReplacingByEmptyFileBlock(
id = target.id,
type = type
)
} else {
proceedWithCreatingEmptyFileBlock(
id = target.id,
type = type
)
}
}
private fun proceedWithCreatingEmptyFileBlock(
@ -1249,6 +1263,22 @@ class PageViewModel(
}
}
private fun proceedWithReplacingByEmptyFileBlock(
id: String,
type: Content.File.Type,
state: Content.File.State = Content.File.State.EMPTY
) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Replace(
context = context,
target = id,
prototype = Prototype.File(type = type, state = state)
)
)
}
}
fun onCheckboxClicked(view: BlockView.Text.Checkbox) {
blocks = blocks.map { block ->
@ -1511,17 +1541,33 @@ class PageViewModel(
}
fun onAddDividerBlockClicked() {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Create(
context = context,
target = orchestrator.stores.focus.current().id,
position = Position.BOTTOM,
prototype = Prototype.Divider
val target = blocks.first { it.id == orchestrator.stores.focus.current().id }
val content = target.content
if (content is Content.Text && content.text.isEmpty()) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Replace(
context = context,
target = target.id,
prototype = Prototype.Divider
)
)
)
}
} else {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Create(
context = context,
target = target.id,
position = Position.BOTTOM,
prototype = Prototype.Divider
)
)
}
}
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
}
private fun proceedWithUpdatingTextStyle(
@ -1639,17 +1685,32 @@ class PageViewModel(
}
fun onAddBookmarkBlockClicked() {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Create(
context = context,
position = Position.BOTTOM,
target = orchestrator.stores.focus.current().id,
prototype = Prototype.Bookmark
val target = blocks.first { it.id == orchestrator.stores.focus.current().id }
val content = target.content
if (content is Content.Text && content.text.isEmpty()) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Replace(
context = context,
target = target.id,
prototype = Prototype.Bookmark
)
)
)
}
} else {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.CRUD.Create(
context = context,
position = Position.BOTTOM,
target = target.id,
prototype = Prototype.Bookmark
)
)
}
}
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
}
fun onAddBookmarkUrl(target: String, url: String) {

View file

@ -0,0 +1,189 @@
package com.agileburo.anytype.presentation.page.editor
import MockDataFactory
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.agileburo.anytype.domain.base.Either
import com.agileburo.anytype.domain.block.interactor.CreateBlock
import com.agileburo.anytype.domain.block.interactor.ReplaceBlock
import com.agileburo.anytype.domain.block.model.Block
import com.agileburo.anytype.domain.block.model.Position
import com.agileburo.anytype.domain.event.model.Payload
import com.agileburo.anytype.presentation.util.CoroutinesTestRule
import com.nhaarman.mockitokotlin2.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.MockitoAnnotations
class EditorAddBlockTest : EditorPresentationTestSetup() {
@get:Rule
val rule = InstantTaskExecutorRule()
@get:Rule
val coroutineTestRule = CoroutinesTestRule()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
}
@Test
fun `should replace currently focused text block instead of adding a new block after this text block if this text block is empty`() {
// SETUP
val text = ""
val block = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
text = text,
marks = emptyList(),
style = Block.Content.Text.Style.values().random()
),
children = emptyList()
)
val page = listOf(
Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Page(
style = Block.Content.Page.Style.SET
),
children = listOf(block.id)
),
block
)
val newStyle = Block.Content.Text.Style.values().random()
val params = ReplaceBlock.Params(
context = root,
target = block.id,
prototype = Block.Prototype.Text(
style = newStyle
)
)
stubInterceptEvents()
stubOpenDocument(document = page)
stubReplaceBlock(params)
val vm = buildViewModel()
// TESTING
vm.apply {
onStart(root)
onBlockFocusChanged(id = block.id, hasFocus = true)
onAddBlockToolbarClicked()
}
// User is now selecting a new text block
vm.onAddTextBlockClicked(style = newStyle)
verifyZeroInteractions(createBlock)
verifyBlocking(replaceBlock, times(1)) { invoke(params) }
}
@Test
fun `should add new text block after currently focused text block if this focused text block is not empty`() {
// SETUP
val text = MockDataFactory.randomString()
val block = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
text = text,
marks = emptyList(),
style = Block.Content.Text.Style.values().random()
),
children = emptyList()
)
val page = listOf(
Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Page(
style = Block.Content.Page.Style.SET
),
children = listOf(block.id)
),
block
)
val newStyle = Block.Content.Text.Style.values().random()
val params = CreateBlock.Params(
context = root,
target = block.id,
prototype = Block.Prototype.Text(
style = newStyle
),
position = Position.BOTTOM
)
stubInterceptEvents()
stubOpenDocument(document = page)
stubCreateBlock(params)
val vm = buildViewModel()
// TESTING
vm.apply {
onStart(root)
onBlockFocusChanged(id = block.id, hasFocus = true)
onAddBlockToolbarClicked()
}
// User is now selecting a new text block
vm.onAddTextBlockClicked(style = newStyle)
verifyZeroInteractions(replaceBlock)
verifyBlocking(createBlock, times(1)) { invoke(params) }
}
private fun stubReplaceBlock(
params: ReplaceBlock.Params
) {
replaceBlock.stub {
onBlocking { invoke(params) } doReturn Either.Right(
Pair(
MockDataFactory.randomUuid(),
Payload(
context = root,
emptyList()
)
)
)
}
}
private fun stubCreateBlock(
params: CreateBlock.Params
) {
createBlock.stub {
onBlocking { invoke(params) } doReturn Either.Right(
Pair(
MockDataFactory.randomUuid(),
Payload(
context = root,
emptyList()
)
)
)
}
}
}