1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-12 10:40:25 +09:00

Editor | Fix | Note title (#1930)

* legacy

* update note title logic

* tests

* pr fix

* pr fixes
This commit is contained in:
Konstantin Ivanov 2021-11-15 17:44:51 +04:00 committed by GitHub
parent ecdcc2cde5
commit 1776331d42
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 184 additions and 56 deletions

View file

@ -87,6 +87,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
import com.anytypeio.anytype.presentation.editor.editor.sam.ScrollAndMoveTarget
import com.anytypeio.anytype.presentation.editor.editor.sam.ScrollAndMoveTargetDescriptor
import com.anytypeio.anytype.presentation.editor.markup.MarkupColorView
import com.anytypeio.anytype.presentation.editor.model.EditorFooter
import com.anytypeio.anytype.ui.alert.AlertUpdateAppFragment
import com.anytypeio.anytype.ui.base.NavigationFragment
import com.anytypeio.anytype.ui.editor.cover.CoverSliderObjectFragment
@ -207,6 +208,9 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
}
private val footerMentionDecorator by lazy { MentionFooterItemDecorator(screen) }
private val noteHeaderDecorator by lazy {
NoteHeaderItemDecorator(offset = dimen(R.dimen.default_note_title_offset))
}
private val markupColorToolbarFooter by lazy { MarkupColorToolbarFooter(screen) }
private val slashWidgetFooter by lazy { SlashWidgetFooterItemDecorator(screen) }
private val styleToolbarFooter by lazy { StyleToolbarItemDecorator(screen) }
@ -738,6 +742,20 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
}
}
}
jobs += subscribe(vm.footers) { footer ->
when (footer) {
EditorFooter.None -> {
if (recycler.containsItemDecoration(noteHeaderDecorator)) {
recycler.removeItemDecoration(noteHeaderDecorator)
}
}
EditorFooter.Note -> {
if (!recycler.containsItemDecoration(noteHeaderDecorator)) {
recycler.addItemDecoration(noteHeaderDecorator)
}
}
}
}
}
}

View file

@ -46,7 +46,6 @@ object BlockActionToolbarFactory {
is BlockView.Description -> TODO()
is BlockView.FeaturedRelation -> TODO()
is BlockView.Unsupported -> TODO()
is BlockView.TitleNote -> TODO()
is BlockView.Latex -> TODO()
is BlockView.LinkToObject.Archived -> TODO()
is BlockView.LinkToObject.Deleted -> TODO()

View file

@ -158,6 +158,7 @@ data class Block(
enum class Type { ROW, COLUMN, DIV, HEADER }
}
@Deprecated("Legacy class")
data class Page(val style: Style) : Content() {
enum class Style { EMPTY, TASK, SET }
}

View file

@ -63,7 +63,6 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HEADER_TWO
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HIGHLIGHT
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_LATEX
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_NOTE_TITLE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_NUMBERED
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_ARCHIVE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_DEFAULT
@ -255,15 +254,6 @@ class BlockAdapter(
}
}
}
HOLDER_NOTE_TITLE -> {
TitleNoteHolder(
view = inflater.inflate(
R.layout.item_block_note_title,
parent,
false
)
)
}
HOLDER_HEADER_ONE -> {
HeaderOne(
view = inflater.inflate(

View file

@ -1,6 +0,0 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.other
import android.view.View
import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
class TitleNoteHolder(view: View) : BlockViewHolder(view)

View file

@ -0,0 +1,20 @@
package com.anytypeio.anytype.core_ui.tools
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class NoteHeaderItemDecorator(private val offset: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view)
if (position == 0) {
outRect.top = offset
}
}
}

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="124dp">
</FrameLayout>

View file

@ -57,6 +57,8 @@
<dimen name="default_toolbar_color_item_divider_width">1dp</dimen>
<dimen name="default_toolbar_color_placeholder_text_size">22sp</dimen>
<dimen name="default_note_title_offset">124dp</dimen>
<dimen name="modal_rect_margin_top">6dp</dimen>
<dimen name="modal_main_margin_start">20dp</dimen>
<dimen name="modal_main_margin_end">20dp</dimen>

View file

@ -85,6 +85,7 @@ import com.anytypeio.anytype.presentation.editor.editor.slash.SlashExtensions.ge
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashExtensions.getSlashWidgetStyleItems
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingMode
import com.anytypeio.anytype.presentation.editor.model.EditorFooter
import com.anytypeio.anytype.presentation.editor.model.TextUpdate
import com.anytypeio.anytype.presentation.editor.render.BlockViewRenderer
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
@ -99,7 +100,6 @@ import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
import com.anytypeio.anytype.presentation.navigation.SupportNavigation
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
import com.anytypeio.anytype.presentation.objects.SupportedLayouts
import com.anytypeio.anytype.presentation.objects.sortByType
import com.anytypeio.anytype.presentation.objects.toView
import com.anytypeio.anytype.presentation.relations.DocumentRelationView
import com.anytypeio.anytype.presentation.relations.views
@ -172,6 +172,8 @@ class EditorViewModel(
var mode: EditorMode = EditorMode.Edit
val footers = MutableStateFlow<EditorFooter>(EditorFooter.None)
private val controlPanelInteractor = Interactor(viewModelScope)
val controlPanelViewState = MutableLiveData<ControlPanelState>()
@ -459,12 +461,14 @@ class EditorViewModel(
orchestrator.stores.focus.stream(),
orchestrator.stores.details.stream()
) { models, focus, details ->
val root = models.first { it.id == context }
footers.value = getFooterState(root, details)
models.asMap().render(
mode = mode,
indent = INITIAL_INDENT,
anchor = context,
focus = focus,
root = models.first { it.id == context },
root = root,
details = details,
relations = orchestrator.stores.relations.current(),
restrictions = orchestrator.stores.objectRestrictions.current(),
@ -5401,4 +5405,13 @@ class EditorViewModel(
}
//endregion
//region FOOTER
private fun getFooterState(root: Block, details: Block.Details): EditorFooter {
return when (details.details[root.id]?.layout?.toInt()) {
ObjectType.Layout.NOTE.code -> EditorFooter.Note
else -> EditorFooter.None
}
}
//endregion
}

View file

@ -25,7 +25,6 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HEADER_TWO
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HIGHLIGHT
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_LATEX
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_NOTE_TITLE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_NUMBERED
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_ARCHIVE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_DEFAULT
@ -545,24 +544,6 @@ sealed class BlockView : ViewType, Parcelable {
}
}
/**
* UI-model for a note-layout title block.
* In fact it's just an empty space, because NOTE LAYOUT doesn't have title
* @property id block's id
*/
@Parcelize
data class TitleNote(
override val id: String
) : BlockView() {
override fun getViewType() = HOLDER_NOTE_TITLE
companion object {
const val INTERNAL_ID = "HOLDER_NOTE_TITLE"
}
}
/**
* UI-model for a code-snippet block.
* @property id block's id

View file

@ -6,7 +6,6 @@ object Types {
const val HOLDER_PROFILE_TITLE = 35
const val HOLDER_ARCHIVE_TITLE = 36
const val HOLDER_TODO_TITLE = 48
const val HOLDER_NOTE_TITLE = 50
const val HOLDER_HEADER_ONE = 2
const val HOLDER_HEADER_TWO = 3
const val HOLDER_HEADER_THREE = 4

View file

@ -0,0 +1,6 @@
package com.anytypeio.anytype.presentation.editor.model
sealed class EditorFooter {
object Note : EditorFooter()
object None : EditorFooter()
}

View file

@ -54,9 +54,6 @@ class DefaultBlockViewRenderer(
)
)
}
if (isLayoutNote(root, details)) {
result.add(BlockView.TitleNote(id = BlockView.TitleNote.INTERNAL_ID))
}
}
}
@ -1399,9 +1396,4 @@ class DefaultBlockViewRenderer(
is Cursor.Range -> cursor.range.first
}
}
private fun isLayoutNote(root: Block, details: Block.Details): Boolean {
val layoutCode = details.details[root.id]?.layout?.toInt()
return layoutCode == ObjectType.Layout.NOTE.code
}
}

View file

@ -3,19 +3,24 @@ 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.*
import com.anytypeio.anytype.presentation.MockTypicalDocumentFactory
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.model.EditorFooter
import com.anytypeio.anytype.presentation.relations.DocumentRelationView
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.jraska.livedata.test
import kotlinx.coroutines.runBlocking
import net.lachlanmckee.timberjunit.TimberTestRule
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.MockitoAnnotations
import kotlin.test.assertEquals
import kotlin.time.ExperimentalTime
class EditorNoteLayoutTest : EditorPresentationTestSetup() {
@ -43,8 +48,9 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
coroutineTestRule.advanceTime(EditorViewModel.TEXT_CHANGES_DEBOUNCE_DURATION)
}
@ExperimentalTime
@Test
fun `should render note title block with featured relations block`() {
fun `should render featured relations block and show note header as footer`() = runBlocking {
val featuredBlock = Block(
id = "featuredRelations",
@ -128,9 +134,6 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
vm.onStart(root)
val expected = listOf(
BlockView.TitleNote(
id = BlockView.TitleNote.INTERNAL_ID
),
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
@ -146,6 +149,121 @@ class EditorNoteLayoutTest : EditorPresentationTestSetup() {
)
vm.state.test().assertValue(ViewState.Success(expected))
vm.footers.test {
val result = awaitItem()
assertEquals(EditorFooter.Note, result)
cancelAndConsumeRemainingEvents()
}
}
@ExperimentalTime
@Test
fun `should render featured relations block and not show note header as footer`() = runBlocking {
val featuredBlock = Block(
id = "featuredRelations",
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.FeaturedRelations
)
val header = Block(
id = "header",
content = Block.Content.Layout(
type = Block.Content.Layout.Type.HEADER
),
fields = Block.Fields.empty(),
children = listOf(featuredBlock.id)
)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
content = Block.Content.Smart(SmartBlockType.PAGE),
children = listOf(header.id)
)
val doc = listOf(page, header, featuredBlock)
val objectTypeId = "objectTypeId"
val objectTypeName = "objectTypeName"
val objectTypeDescription = "objectTypeDesc"
val r1 = MockTypicalDocumentFactory.relation("Ad")
val r2 = MockTypicalDocumentFactory.relation("De")
val r3 = MockTypicalDocumentFactory.relation("HJ")
val relationObjectType = Relation(
key = Block.Fields.TYPE_KEY,
name = "Object Type",
format = Relation.Format.OBJECT,
source = Relation.Source.DERIVED
)
val value1 = MockDataFactory.randomString()
val value2 = MockDataFactory.randomString()
val value3 = MockDataFactory.randomString()
val objectFields = Block.Fields(
mapOf(
r1.key to value1,
r2.key to value2,
r3.key to value3,
relationObjectType.key to objectTypeId,
Relations.FEATURED_RELATIONS to listOf(relationObjectType.key),
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble()
)
)
val objectTypeFields = Block.Fields(
mapOf(
Block.Fields.NAME_KEY to objectTypeName,
Block.Fields.DESCRIPTION_KEY to objectTypeDescription
)
)
val customDetails = Block.Details(
mapOf(
root to objectFields,
objectTypeId to objectTypeFields
)
)
stubInterceptEvents()
stubInterceptThreadStatus()
stubGetObjectTypes(objectTypes = listOf())
stubGetDefaultObjectType()
stubOpenDocument(
document = doc,
details = customDetails,
relations = listOf(r1, r2, r3, relationObjectType)
)
val vm = buildViewModel()
vm.onStart(root)
val expected = listOf(
BlockView.FeaturedRelation(
id = featuredBlock.id,
relations = listOf(
DocumentRelationView.ObjectType(
relationId = relationObjectType.key,
name = objectTypeName,
value = null,
isFeatured = true,
type = objectTypeId
)
)
)
)
vm.state.test().assertValue(ViewState.Success(expected))
vm.footers.test {
val result = awaitItem()
assertEquals(EditorFooter.None, result)
cancelAndConsumeRemainingEvents()
}
}
}