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

DROID-122 Editor | Enhancement | Simple tables, cell menu, cells selecting (#2696)

* DROID-132 cell selection state + logic + tests

* DROID-132 added cell border, cell rect classes

* DROID-132 refactoring

* DROID-132 refactoring

* DROID-132 cell selection decoration

* DROID-132 remove legacy

* DROID-132 table block holder, add selection decoration

* DROID-132 delete legacy clicks

* DROID-132 cell model update

* DROID-132 simple table widget events

* DROID-132 editor table mode

* DROID-132 table cell extension

* DROID-132 legacy classes

* DROID-132 delete legacy

* DROID-132 update cell clicks

* DROID-132 cells extensions

* DROID-132 update item decorations

* DROID-132 cell selection top toolbar

* DROID-132 select cells, show widget

* DROID-132 clear content command

* DROID-132 clear content use case

* DROID-132 menu item click

* DROID-132 table delegate

* DROID-132 fixes

* DROID-132 refactoring

* DROID-132 fix hide simple widget event

* DROID-132 refactoring

* DROID-132 test fixes

* DROID-132 fix tests

* DROID-132 turn off buttons

* DROID-122 delete legacy delegate

* DROID-122 table block diff util

* DROID-122 add selected cells to table view model

* DROID-122 rename param

* DROID-122 add selection state to table block holder

* DROID-122 delete

* DROID-122 mapping

* DROID-122 rename funcs

* DROID-122 fix tests

* DROID-122 update selection logic

* DROID-122 pr fix

* DROID-122 code style

* DROID-122 pr fixes

Co-authored-by: konstantiniiv <ki@anytype.io>
This commit is contained in:
Konstantin Ivanov 2022-11-09 10:47:09 +01:00 committed by GitHub
parent eecdc5e690
commit b30d60f4df
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1735 additions and 340 deletions

View file

@ -20,6 +20,7 @@ import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
import com.anytypeio.anytype.domain.block.interactor.DuplicateBlock
import com.anytypeio.anytype.domain.block.interactor.MergeBlocks
@ -88,7 +89,6 @@ import com.anytypeio.anytype.presentation.editor.editor.InternalDetailModificati
import com.anytypeio.anytype.presentation.editor.editor.Orchestrator
import com.anytypeio.anytype.presentation.editor.editor.Proxy
import com.anytypeio.anytype.presentation.editor.editor.pattern.DefaultPatternMatcher
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
import com.anytypeio.anytype.presentation.editor.template.DefaultEditorTemplateDelegate
@ -190,6 +190,7 @@ open class EditorTestSetup {
lateinit var turnIntoStyle: TurnIntoStyle
lateinit var setObjectType: SetObjectType
lateinit var objectToSet: ConvertObjectToSet
lateinit var clearBlockContent: ClearBlockContent
lateinit var getDefaultEditorType: GetDefaultEditorType
@ -243,9 +244,6 @@ open class EditorTestSetup {
@Mock
lateinit var fillTableRow: FillTableRow
@Mock
lateinit var simpleTableDelegate: SimpleTableDelegate
val root: String = "rootId123"
private val urlBuilder by lazy {
@ -289,6 +287,7 @@ open class EditorTestSetup {
getSearchObjects = SearchObjects(repo)
interceptThreadStatus = InterceptThreadStatus(channel = threadStatusChannel)
downloadUnsplashImage = DownloadUnsplashImage(unsplashRepository)
clearBlockContent = ClearBlockContent(repo)
downloadFile = DownloadFile(
downloader = mock(),
context = Dispatchers.Main
@ -388,7 +387,8 @@ open class EditorTestSetup {
setObjectType = setObjectType,
createBookmarkBlock = createBookmarkBlock,
createTable = createTable,
fillTableRow = fillTableRow
fillTableRow = fillTableRow,
clearBlockContent = clearBlockContent
),
createNewDocument = createNewDocument,
interceptThreadStatus = interceptThreadStatus,
@ -409,7 +409,6 @@ open class EditorTestSetup {
setDocImageIcon = setDocImageIcon,
editorTemplateDelegate = editorTemplateDelegate,
createNewObject = createNewObject,
simpleTablesDelegate = simpleTableDelegate,
objectToSet = objectToSet
)
}

View file

@ -18,6 +18,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
import com.anytypeio.anytype.domain.block.interactor.DuplicateBlock
import com.anytypeio.anytype.domain.block.interactor.MergeBlocks
@ -93,8 +94,6 @@ import com.anytypeio.anytype.presentation.editor.editor.Interactor
import com.anytypeio.anytype.presentation.editor.editor.InternalDetailModificationManager
import com.anytypeio.anytype.presentation.editor.editor.Orchestrator
import com.anytypeio.anytype.presentation.editor.editor.pattern.DefaultPatternMatcher
import com.anytypeio.anytype.presentation.editor.editor.table.DefaultSimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
import com.anytypeio.anytype.presentation.editor.template.DefaultEditorTemplateDelegate
@ -224,7 +223,6 @@ object EditorSessionModule {
setDocImageIcon: SetDocumentImageIcon,
editorTemplateDelegate: EditorTemplateDelegate,
createNewObject: CreateNewObject,
simpleTableDelegate: SimpleTableDelegate,
objectToSet: ConvertObjectToSet
): EditorViewModelFactory = EditorViewModelFactory(
openPage = openPage,
@ -257,7 +255,6 @@ object EditorSessionModule {
setDocImageIcon = setDocImageIcon,
editorTemplateDelegate = editorTemplateDelegate,
createNewObject = createNewObject,
simpleTablesDelegate = simpleTableDelegate,
objectToSet = objectToSet
)
@ -285,12 +282,6 @@ object EditorSessionModule {
applyTemplate = applyTemplate
)
@JvmStatic
@Provides
@PerScreen
fun provideSimpleTableDelegate(
): SimpleTableDelegate = DefaultSimpleTableDelegate()
@JvmStatic
@Provides
fun provideDefaultBlockViewRenderer(
@ -366,7 +357,8 @@ object EditorSessionModule {
setRelationKey: SetRelationKey,
analytics: Analytics,
updateBlocksMark: UpdateBlocksMark,
middlewareShareDownloader: MiddlewareShareDownloader
middlewareShareDownloader: MiddlewareShareDownloader,
clearBlockContent: ClearBlockContent
): Orchestrator = Orchestrator(
stores = storage,
createBlock = createBlock,
@ -408,6 +400,7 @@ object EditorSessionModule {
setObjectType = setObjectType,
createTable = createTable,
fillTableRow = fillTableRow,
clearBlockContent = clearBlockContent
)
}
@ -1000,6 +993,13 @@ object EditorUseCaseModule {
uriFileProvider = fileProvider
)
@JvmStatic
@Provides
@PerScreen
fun provideBlockListClearContent(
repo: BlockRepository
): ClearBlockContent = ClearBlockContent(repo)
@Module
interface Bindings {

View file

@ -109,7 +109,6 @@ import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelStat
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.sam.ScrollAndMoveTarget
import com.anytypeio.anytype.presentation.editor.editor.sam.ScrollAndMoveTargetDescriptor
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetViewState
import com.anytypeio.anytype.presentation.editor.markup.MarkupColorView
import com.anytypeio.anytype.presentation.editor.model.EditorFooter
import com.anytypeio.anytype.presentation.editor.template.SelectTemplateViewState
@ -481,20 +480,6 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
}
}
jobs += subscribe(vm.simpleTablesViewState) { state ->
val behavior = BottomSheetBehavior.from(binding.simpleTableWidget)
when (state) {
is SimpleTableWidgetViewState.Active -> {
binding.simpleTableWidget.onStateChanged(state = state.state)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.addBottomSheetCallback(onHideBottomSheetCallback)
}
SimpleTableWidgetViewState.Idle -> {
behavior.removeBottomSheetCallback(onHideBottomSheetCallback)
behavior.state = BottomSheetBehavior.STATE_HIDDEN
}
}
}
}
vm.onStart(id = extractDocumentId())
super.onStart()
@ -615,6 +600,13 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
.onEach { vm.onExitMultiSelectModeClicked() }
.launchIn(lifecycleScope)
binding.cellSelectionTopToolbar
.doneButton
.clicks()
.throttleFirst()
.onEach { vm.onCellsSelectionDoneClick() }
.launchIn(lifecycleScope)
binding.bottomToolbar
.homeClicks()
.onEach { vm.onHomeButtonClicked() }
@ -677,6 +669,10 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
}
binding.simpleTableWidget.setListener {
vm.onSimpleTableWidgetItemClicked(it)
}
binding.undoRedoToolbar.undo.clicks()
.throttleFirst()
.onEach {
@ -755,6 +751,8 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
BottomSheetBehavior.STATE_HIDDEN
BottomSheetBehavior.from(binding.typeHasTemplateToolbar).state =
BottomSheetBehavior.STATE_HIDDEN
BottomSheetBehavior.from(binding.simpleTableWidget).state =
BottomSheetBehavior.STATE_HIDDEN
observeNavBackStack()
}
@ -1558,6 +1556,46 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
binding.objectTypesToolbar.clear()
}
}
state.simpleTableWidget.apply {
val behavior = BottomSheetBehavior.from(binding.simpleTableWidget)
if (isVisible) {
binding.simpleTableWidget.onStateChanged(
cellItems = state.simpleTableWidget.cellItems,
rowItems = state.simpleTableWidget.rowItems,
columnItems = state.simpleTableWidget.columnItems
)
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
keyboardDelayJobs += lifecycleScope.launch {
if (binding.recycler.itemDecorationCount == 0) {
binding.recycler.addItemDecoration(styleToolbarFooter)
}
proceedWithHidingSoftInput()
delayKeyboardHide(insets)
behavior.apply {
setState(BottomSheetBehavior.STATE_EXPANDED)
addBottomSheetCallback(onHideBottomSheetCallback)
}
}
}
} else {
if (behavior.state == BottomSheetBehavior.STATE_EXPANDED) {
behavior.removeBottomSheetCallback(onHideBottomSheetCallback)
behavior.state = BottomSheetBehavior.STATE_HIDDEN
}
}
}
state.cellsSelectTopWidget.apply {
if (isVisible) {
binding.cellSelectionTopToolbar.showWithAnimation()
binding.cellSelectionTopToolbar.setCellSelectionText(count)
} else {
binding.cellSelectionTopToolbar.hideWithAnimation {
if (hasBinding) binding.topToolbar.visible()
}
}
}
}
private fun applySlideTransition(transTarget: View, transDuration: Long, transRoot: ViewGroup) {

View file

@ -58,6 +58,16 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.anytypeio.anytype.core_ui.widgets.toolbar.MultiSelectTopToolbarWidget
android:id="@+id/cellSelectionTopToolbar"
android:layout_width="0dp"
android:layout_height="48dp"
android:background="@color/defaultCanvasColor"
android:translationY="-48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.anytypeio.anytype.core_ui.widgets.toolbar.BlockToolbarWidget
android:id="@+id/toolbar"
android:layout_width="0dp"

View file

@ -244,6 +244,15 @@ class BlockViewDiffUtil(
}
}
if (newBlock is BlockView.Table && oldBlock is BlockView.Table) {
if (newBlock.cells != oldBlock.cells) {
changes.add(TABLE_CELLS_CHANGED)
}
if (newBlock.selectedCellsIds != oldBlock.selectedCellsIds) {
changes.add(TABLE_CELLS_SELECTION_CHANGED)
}
}
return if (changes.isNotEmpty())
Payload(changes).also { Timber.d("Returning payload: $it") }
else
@ -335,5 +344,8 @@ class BlockViewDiffUtil(
const val DECORATION_CHANGED = 27
const val CALLOUT_ICON_CHANGED = 28
const val TABLE_CELLS_SELECTION_CHANGED = 340
const val TABLE_CELLS_CHANGED = 341
}
}

View file

@ -52,18 +52,11 @@ class TableBlockAdapter(
val block = item.block
if (block == null) {
clickListener(
ListenerType.TableEmptyCell(
cellId = item.getId(),
rowId = item.rowId,
tableId = tableBlockId
)
ListenerType.TableEmptyCell(cell = item)
)
} else {
clickListener(
ListenerType.TableTextCell(
tableId = tableBlockId,
cellId = block.id
)
ListenerType.TableTextCell(cell = item)
)
}
}

View file

@ -60,10 +60,7 @@ class TableEditableCellsAdapter(
val pos = bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION) {
clicked(
ListenerType.TableTextCell(
tableId = tableBlockId,
cellId = items[pos].getId()
)
ListenerType.TableTextCell(cell = items[pos])
)
}
}
@ -123,13 +120,8 @@ class TableEditableCellsAdapter(
itemView.setOnClickListener {
val pos = bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION) {
val item = items[bindingAdapterPosition]
clicked(
ListenerType.TableEmptyCell(
cellId = item.getId(),
rowId = item.rowId,
tableId = tableBlockId
)
ListenerType.TableEmptyCell(cell = items[pos])
)
}
}

View file

@ -17,11 +17,14 @@ import com.anytypeio.anytype.core_ui.layout.TableHorizontalItemDivider
import com.anytypeio.anytype.core_ui.layout.TableVerticalItemDivider
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_ui.features.table.TableEditableCellsAdapter
import com.anytypeio.anytype.core_ui.layout.TableCellSelectionDecoration
import com.anytypeio.anytype.core_utils.ext.containsItemDecoration
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionEvent
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
import com.anytypeio.anytype.presentation.editor.selection.TableCellsSelectionState
class TableBlockHolder(
binding: ItemBlockTableBinding,
@ -37,6 +40,12 @@ class TableBlockHolder(
val recycler: RecyclerView = binding.recyclerTable
private val selected = binding.selected
private val cellsSelectionState = TableCellsSelectionState()
private val cellSelectionDecoration: TableCellSelectionDecoration = TableCellSelectionDecoration(
drawable = binding.root.context.drawable(R.drawable.cell_top_border)
)
private val tableAdapter = TableBlockAdapter(
differ = TableCellsDiffUtil,
clickListener = clickListener
@ -90,6 +99,7 @@ class TableBlockHolder(
(lm as CustomGridLayoutManager).spanCount = item.rowCount
tableEditableCellsAdapter.setTableBlockId(item.id)
tableEditableCellsAdapter.updateWithDiffUtil(item.cells)
updateCellsSelection(item)
} else {
(lm as GridLayoutManager).spanCount = item.rowCount
tableAdapter.setTableBlockId(item.id)
@ -102,12 +112,18 @@ class TableBlockHolder(
item: BlockView.Table
) {
payloads.forEach { payload ->
if (payload.changes.contains(BlockViewDiffUtil.TABLE_CELLS_CHANGED)) {
bind(item)
}
if (payload.changes.contains(BlockViewDiffUtil.SELECTION_CHANGED)) {
selected.isSelected = item.isSelected
}
if (payload.changes.contains(BlockViewDiffUtil.BACKGROUND_COLOR_CHANGED)) {
applyBackground(item.background)
}
if (payload.changes.contains(BlockViewDiffUtil.TABLE_CELLS_SELECTION_CHANGED)) {
updateCellsSelection(item)
}
}
}
@ -118,4 +134,29 @@ class TableBlockHolder(
fun recycle() {
tableAdapter.submitList(emptyList())
}
private fun updateCellsSelection(item: BlockView.Table) {
if (item.selectedCellsIds.isEmpty()) {
cellsSelectionState.clear()
if (recycler.containsItemDecoration(cellSelectionDecoration)) {
recycler.removeItemDecoration(cellSelectionDecoration)
}
} else {
val selectedCells = item.cells.filter { cell ->
item.selectedCellsIds.contains(cell.getId())
}
cellsSelectionState.clear()
cellsSelectionState.set(cells = selectedCells)
if (cellsSelectionState.current().isNotEmpty()) {
cellSelectionDecoration.setSelectionState(cellsSelectionState.current())
if (!recycler.containsItemDecoration(cellSelectionDecoration)) {
recycler.addItemDecoration(cellSelectionDecoration)
} else {
recycler.invalidateItemDecorations()
}
} else {
recycler.removeItemDecoration(cellSelectionDecoration)
}
}
}
}

View file

@ -0,0 +1,82 @@
package com.anytypeio.anytype.core_ui.layout
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import kotlin.math.roundToInt
class TableCellSelectionDecoration(
private val drawable: Drawable
) : RecyclerView.ItemDecoration() {
private val selectionState: MutableList<BlockView.Table.CellSelection> = mutableListOf()
fun setSelectionState(newState: List<BlockView.Table.CellSelection>) {
selectionState.clear()
selectionState.addAll(newState)
}
override fun onDraw(
canvas: Canvas,
parent: RecyclerView,
state: RecyclerView.State
) {
canvas.save()
val rect = Rect()
parent.children.forEach { view ->
val position = parent.getChildAdapterPosition(view)
if (position != RecyclerView.NO_POSITION) {
val cellSelection = selectionState.find { it.cellIndex == position }
if (cellSelection != null) {
parent.getDecoratedBoundsWithMargins(view, rect)
if (cellSelection.left) {
drawable.setBounds(
rect.left,
rect.top,
rect.left + drawable.intrinsicWidth,
rect.bottom
)
drawable.draw(canvas)
}
if (cellSelection.top) {
val top = rect.top + view.translationY.roundToInt()
val bottom = top + drawable.intrinsicHeight
drawable.setBounds(
rect.left,
top,
rect.right,
bottom
)
drawable.draw(canvas)
}
if (cellSelection.right) {
val right = rect.right + view.translationX.roundToInt()
val left = right - drawable.intrinsicWidth
drawable.setBounds(
left,
rect.top,
right,
rect.bottom
)
drawable.draw(canvas)
}
if (cellSelection.bottom) {
val bottom = rect.bottom + view.translationY.roundToInt()
val top = bottom - drawable.intrinsicHeight
drawable.setBounds(
rect.left,
top,
rect.right,
bottom
)
drawable.draw(canvas)
}
}
}
}
canvas.restore()
}
}

View file

@ -1,12 +1,17 @@
package com.anytypeio.anytype.core_ui.widgets.toolbar
import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.animation.doOnEnd
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.WidgetMultiSelectTopToolbarBinding
import com.anytypeio.anytype.core_utils.ext.dimen
class MultiSelectTopToolbarWidget @JvmOverloads constructor(
context: Context,
@ -19,4 +24,52 @@ class MultiSelectTopToolbarWidget @JvmOverloads constructor(
val selectText get() : TextView = binding.tvToolbarTitle
val doneButton get() : View = binding.btnDone
fun setCellSelectionText(count: Int) {
when {
count == 1 -> {
selectText.text = context.getString(R.string.one_selected_cell)
}
count > 1 -> {
selectText.text = context.getString(R.string.number_selected_cells, count)
}
else -> {
selectText.text = null
}
}
}
fun showWithAnimation() {
if (translationY < 0) {
ObjectAnimator.ofFloat(
this,
SELECT_BUTTON_ANIMATION_PROPERTY,
0f
).apply {
duration = SELECT_BUTTON_ANIMATION_DURATION
interpolator = DecelerateInterpolator()
start()
}
}
}
fun hideWithAnimation(action: () -> Unit) {
if (translationY >= 0) {
ObjectAnimator.ofFloat(
this,
SELECT_BUTTON_ANIMATION_PROPERTY,
-context.dimen(R.dimen.dp_48)
).apply {
duration = SELECT_BUTTON_ANIMATION_DURATION
interpolator = DecelerateInterpolator()
doOnEnd { action.invoke() }
start()
}
}
}
companion object {
const val SELECT_BUTTON_ANIMATION_PROPERTY = "translationY"
const val SELECT_BUTTON_ANIMATION_DURATION = 200L
}
}

View file

@ -6,7 +6,8 @@ import android.view.LayoutInflater
import androidx.cardview.widget.CardView
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.WidgetSimpleTableBinding
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetState
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetItem
import com.anytypeio.anytype.presentation.editor.markup.MarkupColorView
import com.google.android.material.tabs.TabLayoutMediator
class SimpleTableSettingWidget @JvmOverloads constructor(
@ -19,12 +20,14 @@ class SimpleTableSettingWidget @JvmOverloads constructor(
LayoutInflater.from(context), this, true
)
var onItemClickListener: (SimpleTableWidgetItem) -> Unit = {}
private val cellAdapter = SimpleTableWidgetAdapter(items = listOf(),
onClick = { item -> })
onClick = { item -> onItemClickListener.invoke(item)})
private val columnAdapter = SimpleTableWidgetAdapter(items = listOf(),
onClick = { item -> })
onClick = { item -> onItemClickListener.invoke(item)})
private val rowAdapter = SimpleTableWidgetAdapter(items = listOf(),
onClick = { item -> })
onClick = { item -> onItemClickListener.invoke(item)})
private val pagerAdapter = SimpleTableSettingAdapter(
cellAdapter = cellAdapter,
@ -32,15 +35,18 @@ class SimpleTableSettingWidget @JvmOverloads constructor(
rowAdapter = rowAdapter
)
fun onStateChanged(state: SimpleTableWidgetState) {
when (state) {
is SimpleTableWidgetState.UpdateItems -> {
cellAdapter.update(state.cellItems)
columnAdapter.update(state.columnItems)
rowAdapter.update(state.rowItems)
}
SimpleTableWidgetState.Idle -> {}
}
fun onStateChanged(
cellItems: List<SimpleTableWidgetItem>,
rowItems: List<SimpleTableWidgetItem>,
columnItems: List<SimpleTableWidgetItem>
) {
cellAdapter.update(cellItems)
columnAdapter.update(columnItems)
rowAdapter.update(rowItems)
}
fun setListener(listener: (SimpleTableWidgetItem) -> Unit = {}) {
onItemClickListener = listener
}
init {

View file

@ -23,9 +23,11 @@ class SimpleTableWidgetAdapter(
val holder = VH(
binding = ItemSimpleTableActionBinding.inflate(inflater, parent, false)
).apply {
val pos = bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION)
onClick(items[pos])
itemView.setOnClickListener {
val pos = bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION)
onClick(items[pos])
}
}
return holder
}

View file

@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="2dp"
android:height="2dp" />
<solid android:color="@color/amber_80" />
</shape>

View file

@ -563,6 +563,8 @@
<string name="send_email">Send email</string>
<string name="call_phone_number">Call phone number</string>
<string name="bookmark_icon">Bookmark icon</string>
<string name="one_selected_cell">1 cell selected</string>
<string name="number_selected_cells">%1$d cells selected</string>
<string name="error_find_block">Could\'t find the selected block</string>
<string name="error_block_selection">Block selection error</string>

View file

@ -1101,6 +1101,8 @@ class BlockViewSearchTextTest {
val row2Block2 = StubParagraph(id = "$rowId2-$columnId2", text = "bb2")
val row2Block3 = StubParagraph(id = "$rowId2-$columnId3", text = "bc3")
val tableId = MockDataFactory.randomUuid()
val cells = listOf(
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1110,7 +1112,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 0,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1120,7 +1124,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 2,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1130,7 +1136,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 4,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1140,7 +1148,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 1,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1150,7 +1160,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 3,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1160,7 +1172,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 5,
tableId = tableId
)
)
@ -1170,15 +1184,14 @@ class BlockViewSearchTextTest {
BlockView.Table.Column(id = columnId3, background = ThemeColor.DEFAULT)
)
val tableId = MockDataFactory.randomUuid()
val views = listOf<BlockView>(
BlockView.Table(
id = tableId,
cells = cells,
columns = columns,
rowCount = 2,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
)
@ -1198,7 +1211,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 0,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1215,7 +1230,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 2,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1232,7 +1249,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 4,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1249,7 +1268,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 1,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1266,7 +1287,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 3,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1283,7 +1306,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 5,
tableId = tableId
)
)
@ -1306,7 +1331,8 @@ class BlockViewSearchTextTest {
cells = expectedCells,
columns = columns,
rowCount = 2,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
)
@ -1331,6 +1357,8 @@ class BlockViewSearchTextTest {
val row2Block2 = StubParagraph(id = "$rowId2-$columnId2", text = "bb2")
val row2Block3 = StubParagraph(id = "$rowId2-$columnId3", text = "bc3")
val tableId = MockDataFactory.randomUuid()
val cells = listOf(
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1340,7 +1368,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 0,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1350,7 +1380,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 2,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1360,7 +1392,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 4,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1370,7 +1404,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 1,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1380,7 +1416,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 3,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1390,7 +1428,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 5,
tableId = tableId
)
)
@ -1400,15 +1440,14 @@ class BlockViewSearchTextTest {
BlockView.Table.Column(id = columnId3, background = ThemeColor.DEFAULT)
)
val tableId = MockDataFactory.randomUuid()
val views = listOf<BlockView>(
BlockView.Table(
id = tableId,
cells = cells,
columns = columns,
rowCount = 2,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
)
@ -1421,7 +1460,9 @@ class BlockViewSearchTextTest {
rowId = rowId1,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 0,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1432,6 +1473,8 @@ class BlockViewSearchTextTest {
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 2,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1442,6 +1485,8 @@ class BlockViewSearchTextTest {
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 4,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1451,7 +1496,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 1,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1461,7 +1508,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 3,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -1471,7 +1520,9 @@ class BlockViewSearchTextTest {
rowId = rowId2,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 5,
tableId = tableId
)
)
@ -1496,7 +1547,8 @@ class BlockViewSearchTextTest {
cells = expectedCells,
columns = columns,
rowCount = 2,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
)

View file

@ -348,7 +348,9 @@ fun StubTwoRowsThreeColumnsSimpleTable(
rowId = rowId1,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 0,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -358,7 +360,9 @@ fun StubTwoRowsThreeColumnsSimpleTable(
rowId = rowId1,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 2,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -368,7 +372,9 @@ fun StubTwoRowsThreeColumnsSimpleTable(
rowId = rowId1,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 4,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -378,7 +384,9 @@ fun StubTwoRowsThreeColumnsSimpleTable(
rowId = rowId2,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 1,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -388,7 +396,9 @@ fun StubTwoRowsThreeColumnsSimpleTable(
rowId = rowId2,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 3,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -398,7 +408,9 @@ fun StubTwoRowsThreeColumnsSimpleTable(
rowId = rowId2,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 5,
tableId = tableId
)
)
@ -413,7 +425,8 @@ fun StubTwoRowsThreeColumnsSimpleTable(
cells = cells,
columns = columns,
rowCount = 2,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
}

View file

@ -60,6 +60,7 @@ class TableBlockTest {
val row1Block1 =
StubParagraph(id = "$rowId1-$columnId2", text = "a1")
val row1Block2 = StubParagraph(id = "$rowId1-$columnId3", text = oldText)
val tableId = MockDataFactory.randomUuid()
val cells = listOf(
BlockView.Table.Cell(
@ -67,7 +68,9 @@ class TableBlockTest {
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null
block = null,
cellIndex = 0,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -78,6 +81,8 @@ class TableBlockTest {
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 2,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -88,13 +93,17 @@ class TableBlockTest {
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 4,
tableId = tableId
),
BlockView.Table.Cell(
rowId = rowId1,
columnId = columnId4,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(3),
block = null
block = null,
cellIndex = 6,
tableId = tableId
)
)
@ -104,7 +113,9 @@ class TableBlockTest {
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null
block = null,
cellIndex = 0,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -114,7 +125,9 @@ class TableBlockTest {
rowId = rowId1,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 2,
tableId = tableId
),
BlockView.Table.Cell(
block = BlockView.Text.Paragraph(
@ -124,14 +137,18 @@ class TableBlockTest {
rowId = rowId1,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2)
columnIndex = BlockView.Table.ColumnIndex(2),
cellIndex = 4,
tableId = tableId
),
BlockView.Table.Cell(
rowId = rowId1,
columnId = columnId4,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(3),
block = null
block = null,
cellIndex = 6,
tableId = tableId
)
)
@ -148,14 +165,14 @@ class TableBlockTest {
}
val recycler = givenRecycler(it)
val tableId = MockDataFactory.randomUuid()
val views = listOf<BlockView>(
BlockView.Table(
id = tableId,
cells = cells,
columns = columns,
rowCount = 1,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
)
val adapter = givenAdapter(views)
@ -186,7 +203,8 @@ class TableBlockTest {
cells = cellsNew,
columns = columns,
rowCount = 1,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
)

View file

@ -632,6 +632,10 @@ class BlockDataRepository(
return remote.objectToSet(ctx, source)
}
override suspend fun clearBlockContent(ctx: Id, blockIds: List<Id>) : Payload {
return remote.clearBlockContent(ctx, blockIds)
}
override suspend fun blockDataViewSetSource(
ctx: Id,
block: Id,

View file

@ -258,4 +258,6 @@ interface BlockDataStore {
suspend fun objectToSet(ctx: Id, source: List<String>): Id
suspend fun blockDataViewSetSource(ctx: Id, block: Id, sources: List<String>): Payload
suspend fun clearBlockContent(ctx: Id, blockIds: List<Id>) : Payload
}

View file

@ -257,4 +257,6 @@ interface BlockRemote {
suspend fun objectToSet(ctx: Id, source: List<String>): Id
suspend fun blockDataViewSetSource(ctx: Id, block: Id, sources: List<String>): Payload
suspend fun clearBlockContent(ctx: Id, blockIds: List<Id>) : Payload
}

View file

@ -555,4 +555,8 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
): Payload {
return remote.blockDataViewSetSource(ctx, block, sources)
}
override suspend fun clearBlockContent(ctx: Id, blockIds: List<Id>): Payload {
return remote.clearBlockContent(ctx, blockIds)
}
}

View file

@ -0,0 +1,24 @@
package com.anytypeio.anytype.domain.block.interactor
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class ClearBlockContent(
private val repository: BlockRepository,
) : BaseUseCase<Payload, ClearBlockContent.Params>() {
data class Params(
val ctx: Id,
val blockIds: List<Id>
)
override suspend fun run(params: Params): Either<Throwable, Payload> = safe {
repository.clearBlockContent(
ctx = params.ctx,
blockIds = params.blockIds
)
}
}

View file

@ -323,4 +323,6 @@ interface BlockRepository {
suspend fun objectToSet(ctx: Id, source: List<String>): Id
suspend fun blockDataViewSetSource(ctx: Id, block: Id, sources: List<String>): Payload
suspend fun clearBlockContent(ctx: Id, blockIds: List<Id>) : Payload
}

View file

@ -594,4 +594,8 @@ class BlockMiddleware(
): Payload {
return middleware.blockDataViewSetSource(ctx, block, sources)
}
override suspend fun clearBlockContent(ctx: Id, blockIds: List<Id>): Payload {
return middleware.clearBlockContent(ctx, blockIds)
}
}

View file

@ -1756,6 +1756,21 @@ class Middleware(
return response.event.toPayload()
}
@Throws(Exception::class)
fun clearBlockContent(
ctx: Id,
blockIds: List<Id>
): Payload {
val request = Rpc.BlockText.ListClearContent.Request(
contextId = ctx,
blockIds = blockIds
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.blockListClearContent(request)
if (BuildConfig.DEBUG) logResponse(response)
return response.event.toPayload()
}
private fun logRequest(any: Any) {
val message = "===> " + any::class.java.canonicalName + ":" + "\n" + any.toString()
Timber.d(message)

View file

@ -236,6 +236,10 @@ interface MiddlewareService {
@Throws(Exception::class)
fun blockRelationSetKey(request: Rpc.BlockRelation.SetKey.Request): Rpc.BlockRelation.SetKey.Response
@Throws(Exception::class)
fun blockListClearContent(request: Rpc.BlockText.ListClearContent.Request)
: Rpc.BlockText.ListClearContent.Response
//endregion
//region NAVIGATION commands

View file

@ -438,6 +438,19 @@ class MiddlewareServiceImplementation : MiddlewareService {
}
}
override fun blockListClearContent(request: Rpc.BlockText.ListClearContent.Request): Rpc.BlockText.ListClearContent.Response {
val encoded = Service.blockTextListClearContent(
Rpc.BlockText.ListClearContent.Request.ADAPTER.encode(request)
)
val response = Rpc.BlockText.ListClearContent.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != Rpc.BlockText.ListClearContent.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
override fun blockSplit(request: Rpc.Block.Split.Request): Rpc.Block.Split.Response {
val encoded = Service.blockSplit(Rpc.Block.Split.Request.ADAPTER.encode(request))
val response = Rpc.Block.Split.Response.ADAPTER.decode(encoded)

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.presentation.editor
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Content.Text.Style
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.TextBlock
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Event
@ -10,9 +11,11 @@ import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Reducer
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState.Companion.init
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState.Toolbar
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashWidgetState
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.getSupportedMarkupTypes
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetItem
import com.anytypeio.anytype.presentation.extension.style
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
@ -212,6 +215,18 @@ sealed class ControlPanelMachine {
data class Show(val data: List<ObjectTypeView>) : ObjectTypesWidgetEvent()
object Hide : ObjectTypesWidgetEvent()
}
sealed class SimpleTableWidget : Event() {
data class Show(
val tableId: Id,
val cells: List<BlockView.Table.Cell>,
val cellItems: List<SimpleTableWidgetItem> = emptyList(),
val rowItems: List<SimpleTableWidgetItem> = emptyList(),
val columnItems: List<SimpleTableWidgetItem> = emptyList()
) : SimpleTableWidget()
data class Hide(val tableId: Id) : SimpleTableWidget()
}
}
/**
@ -374,7 +389,8 @@ sealed class ControlPanelMachine {
),
slashWidget = Toolbar.SlashWidget.reset(),
objectTypesToolbar = Toolbar.ObjectTypes.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
styleBackgroundToolbar = Toolbar.Styling.Background.reset(),
simpleTableWidget = Toolbar.SimpleTableWidget.reset()
)
} else {
state.copy(
@ -434,7 +450,7 @@ sealed class ControlPanelMachine {
!state.mainToolbar.isVisible -> state.copy(
mainToolbar = state.mainToolbar.copy(
isVisible = true,
targetBlockType = when(event.style) {
targetBlockType = when (event.style) {
Style.TITLE -> Toolbar.Main.TargetBlockType.Title
else -> Toolbar.Main.TargetBlockType.Any
}
@ -446,12 +462,13 @@ sealed class ControlPanelMachine {
navigationToolbar = Toolbar.Navigation(
isVisible = false
),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
styleBackgroundToolbar = Toolbar.Styling.Background.reset(),
simpleTableWidget = Toolbar.SimpleTableWidget.reset()
)
else -> {
state.copy(
mainToolbar = state.mainToolbar.copy(
targetBlockType = when(event.style) {
targetBlockType = when (event.style) {
Style.TITLE -> Toolbar.Main.TargetBlockType.Title
else -> Toolbar.Main.TargetBlockType.Any
}
@ -462,7 +479,8 @@ sealed class ControlPanelMachine {
mentionToolbar = Toolbar.MentionToolbar.reset(),
slashWidget = Toolbar.SlashWidget.reset(),
objectTypesToolbar = Toolbar.ObjectTypes.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
styleBackgroundToolbar = Toolbar.Styling.Background.reset(),
simpleTableWidget = Toolbar.SimpleTableWidget.reset()
)
}
}
@ -495,6 +513,9 @@ sealed class ControlPanelMachine {
)
)
}
is Event.SimpleTableWidget -> {
handleSimpleTableEvent(event, state)
}
}
private fun handleStylingToolbarEvent(
@ -722,7 +743,8 @@ sealed class ControlPanelMachine {
isVisible = false
),
slashWidget = Toolbar.SlashWidget.reset(),
mentionToolbar = Toolbar.MentionToolbar.reset()
mentionToolbar = Toolbar.MentionToolbar.reset(),
simpleTableWidget = Toolbar.SimpleTableWidget.reset()
)
is Event.MultiSelect.OnExit -> state.copy(
multiSelect = state.multiSelect.copy(
@ -774,7 +796,8 @@ sealed class ControlPanelMachine {
styleTextToolbar = Toolbar.Styling.reset(),
mentionToolbar = Toolbar.MentionToolbar.reset(),
slashWidget = Toolbar.SlashWidget.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
styleBackgroundToolbar = Toolbar.Styling.Background.reset(),
simpleTableWidget = Toolbar.SimpleTableWidget.reset()
)
}
Event.ReadMode.OnExit -> state.copy()
@ -865,6 +888,55 @@ sealed class ControlPanelMachine {
}
}
private fun handleSimpleTableEvent(
event: Event.SimpleTableWidget,
state: ControlPanelState
): ControlPanelState = when (event) {
is Event.SimpleTableWidget.Show -> {
state.copy(
simpleTableWidget = state.simpleTableWidget.copy(
isVisible = true,
tableId = event.tableId,
cells = event.cells,
cellItems = event.cellItems,
rowItems = event.rowItems,
columnItems = event.columnItems
),
cellsSelectTopWidget = state.cellsSelectTopWidget.copy(
isVisible = true,
count = event.cells.size
),
mainToolbar = Toolbar.Main.reset(),
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground.reset(),
styleExtraToolbar = Toolbar.Styling.Extra.reset(),
styleTextToolbar = Toolbar.Styling.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset(),
navigationToolbar = Toolbar.Navigation.reset(),
slashWidget = Toolbar.SlashWidget.reset(),
mentionToolbar = Toolbar.MentionToolbar.reset(),
multiSelect = Toolbar.MultiSelect.reset()
)
}
is Event.SimpleTableWidget.Hide -> {
state.copy(
navigationToolbar = state.navigationToolbar.copy(
isVisible = true
),
multiSelect = Toolbar.MultiSelect.reset(),
mainToolbar = Toolbar.Main.reset(),
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground.reset(),
styleExtraToolbar = Toolbar.Styling.Extra.reset(),
styleTextToolbar = Toolbar.Styling.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset(),
simpleTableWidget = state.simpleTableWidget.copy(
isVisible = false,
tableId = event.tableId
),
cellsSelectTopWidget = Toolbar.CellSelection.reset()
)
}
}
private fun logState(text: String, state: ControlPanelState) {
Timber.i(
"REDUCER, $text STATE:${

View file

@ -75,6 +75,11 @@ interface Editor {
*/
data class Multi(val targets: Set<Id>) : Styling()
}
/**
* Editor in simple table menu mode.
*/
data class Table(val tableId: Id) : Mode()
}
class Storage {

View file

@ -140,10 +140,7 @@ import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleBackgrou
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleColorBackgroundToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleOtherToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleTextToolbarState
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetEvent
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetState
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetViewState
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetItem
import com.anytypeio.anytype.presentation.editor.editor.toCoreModel
import com.anytypeio.anytype.presentation.editor.editor.updateText
import com.anytypeio.anytype.presentation.editor.model.EditorFooter
@ -153,6 +150,9 @@ import com.anytypeio.anytype.presentation.editor.render.BlockViewRenderer
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.search.search
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
import com.anytypeio.anytype.presentation.editor.selection.getSimpleTableWidgetItems
import com.anytypeio.anytype.presentation.editor.selection.toggleTableMode
import com.anytypeio.anytype.presentation.editor.selection.updateTableBlockSelection
import com.anytypeio.anytype.presentation.editor.template.EditorTemplateDelegate
import com.anytypeio.anytype.presentation.editor.template.SelectTemplateEvent
import com.anytypeio.anytype.presentation.editor.template.SelectTemplateState
@ -248,7 +248,6 @@ class EditorViewModel(
private val setDocCoverImage: SetDocCoverImage,
private val setDocImageIcon: SetDocumentImageIcon,
private val templateDelegate: EditorTemplateDelegate,
private val simpleTableDelegate: SimpleTableDelegate,
private val createNewObject: CreateNewObject,
private val objectToSet: ConvertObjectToSet
) : ViewStateViewModel<ViewState>(),
@ -259,7 +258,6 @@ class EditorViewModel(
ToggleStateHolder by renderer,
SelectionStateHolder by orchestrator.memory.selections,
EditorTemplateDelegate by templateDelegate,
SimpleTableDelegate by simpleTableDelegate,
StateReducer<List<Block>, Event> by reducer {
val actions = MutableStateFlow(ActionItemType.defaultSorting)
@ -282,17 +280,6 @@ class EditorViewModel(
}
}
val simpleTablesViewState = simpleTableDelegateState.map { state ->
when (state) {
is SimpleTableWidgetState.UpdateItems -> {
SimpleTableWidgetViewState.Active(
state = state
)
}
SimpleTableWidgetState.Idle -> SimpleTableWidgetViewState.Idle
}
}
val searchResultScrollPosition = MutableStateFlow(NO_SEARCH_RESULT_POSITION)
private val session = MutableStateFlow(Session.IDLE)
@ -2444,7 +2431,7 @@ class EditorViewModel(
if (view == null) {
val cell = views.findTableCellView(target)
if (cell != null) {
onShowSimpleTableWidgetClicked(target)
proceedWithEnterTableMode(cell)
viewModelScope.sendAnalyticsSelectionMenuEvent(analytics)
}
} else {
@ -3842,13 +3829,13 @@ class EditorViewModel(
return
}
proceedWithSelectingCell(
cellId = clicked.cellId,
tableId = clicked.tableId
cellId = clicked.cell.getId(),
tableId = clicked.cell.tableId
)
onTableRowEmptyCellClicked(
cellId = clicked.cellId,
rowId = clicked.rowId,
tableId = clicked.tableId
cellId = clicked.cell.getId(),
rowId = clicked.cell.rowId,
tableId = clicked.cell.tableId
)
}
}
@ -3860,16 +3847,28 @@ class EditorViewModel(
}
}
proceedWithSelectingCell(
cellId = clicked.cellId,
tableId = clicked.tableId
cellId = clicked.cell.getId(),
tableId = clicked.cell.tableId
)
onTableRowEmptyCellClicked(
cellId = clicked.cellId,
rowId = clicked.rowId,
tableId = clicked.tableId
cellId = clicked.cell.getId(),
rowId = clicked.cell.rowId,
tableId = clicked.cell.tableId
)
}
EditorMode.Select -> onBlockMultiSelectClicked(target = clicked.tableId)
EditorMode.Select -> onBlockMultiSelectClicked(target = clicked.cell.tableId)
is EditorMode.Table -> {
val modeTableId = (mode as EditorMode.Table).tableId
val cellTableId = clicked.cell.tableId
if (cellTableId == modeTableId) {
proceedWithClickingOnCellInTableMode(
cell = clicked.cell,
tableId = cellTableId
)
} else {
Timber.e("Cell is from the different table, amend click")
}
}
else -> Unit
}
}
@ -3881,15 +3880,15 @@ class EditorViewModel(
return
}
proceedWithSelectingCell(
cellId = clicked.cellId,
tableId = clicked.tableId
cellId = clicked.cell.getId(),
tableId = clicked.cell.tableId
)
if (!BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
dispatch(
Command.OpenSetBlockTextValueScreen(
ctx = context,
block = clicked.cellId,
table = clicked.tableId
block = clicked.cell.getId(),
table = clicked.cell.tableId
)
)
}
@ -3901,26 +3900,34 @@ class EditorViewModel(
}
if (!BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
proceedWithSelectingCell(
cellId = clicked.cellId,
tableId = clicked.tableId
cellId = clicked.cell.getId(),
tableId = clicked.cell.tableId
)
dispatch(
Command.OpenSetBlockTextValueScreen(
ctx = context,
block = clicked.cellId,
table = clicked.tableId
block = clicked.cell.getId(),
table = clicked.cell.tableId
)
)
}
}
EditorMode.Select -> onBlockMultiSelectClicked(target = clicked.tableId)
EditorMode.Select -> onBlockMultiSelectClicked(target = clicked.cell.tableId)
is EditorMode.Table -> {
val modeTableId = (mode as EditorMode.Table).tableId
val cellTableId = clicked.cell.tableId
if (cellTableId == modeTableId) {
proceedWithClickingOnCellInTableMode(
cell = clicked.cell,
tableId = cellTableId
)
} else {
Timber.e("Cell is from the different table, amend click")
}
}
else -> Unit
}
}
is ListenerType.TableEmptyCellMenu -> {}
is ListenerType.TableTextCellMenu -> {
onShowSimpleTableWidgetClicked(id = clicked.cellId)
}
else -> {}
}
}
@ -6111,13 +6118,121 @@ class EditorViewModel(
//endregion
//region SIMPLE TABLES
private fun onShowSimpleTableWidgetClicked(id: Id) {
viewModelScope.launch {
onSimpleTableEvent(SimpleTableWidgetEvent.onStart(id = id))
fun onCellsSelectionDoneClick() {
proceedWithExitingTableMode()
}
fun onHideSimpleTableWidget() {
proceedWithExitingTableMode()
}
fun onSimpleTableWidgetItemClicked(item: SimpleTableWidgetItem) {
when (item) {
SimpleTableWidgetItem.Cell.ClearContents -> {
val selected = currentSelection()
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.Text.ClearContent(
context = context,
targets = selected.toList()
)
)
}
}
else -> Unit
}
}
fun onHideSimpleTableWidget() {}
/**
* Enter EditorMode.Table
*/
private fun proceedWithEnterTableMode(cell: BlockView.Table.Cell) {
viewModelScope.launch {
mode = EditorMode.Table(tableId = cell.tableId)
clearSelections()
toggleSelection(target = cell.getId())
orchestrator.stores.focus.update(Editor.Focus.empty())
orchestrator.stores.views.update(
views.toggleTableMode(
cellsMode = BlockView.Mode.READ,
selectedCellsIds = currentSelection().toList()
)
)
renderCommand.send(Unit)
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.SimpleTableWidget.Show(
cellItems = listOf(cell).getSimpleTableWidgetItems(),
rowItems = emptyList(),
columnItems = emptyList(),
cells = listOf(cell),
tableId = cell.tableId
)
)
}
}
/**
* Exit EditorMode.Table
*/
private fun proceedWithExitingTableMode() {
Timber.d("proceedWithExitingTableMode, mode:[$mode]")
if (currentSelection().isNotEmpty()) clearSelections()
val currentMode = mode
if (currentMode is EditorMode.Table) {
val tableId = currentMode.tableId
mode = EditorMode.Edit
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.SimpleTableWidget.Hide(
tableId = tableId
)
)
viewModelScope.launch {
orchestrator.stores.views.update(
views.toggleTableMode(
cellsMode = BlockView.Mode.EDIT,
selectedCellsIds = currentSelection().toList()
)
)
renderCommand.send(Unit)
}
} else {
Timber.w("Can't exit Mode.Table, current mode is $mode")
}
}
private fun proceedWithClickingOnCellInTableMode(
tableId: Id,
cell: BlockView.Table.Cell
) {
toggleSelection(target = cell.getId())
if (currentSelection().isEmpty()) {
proceedWithExitingTableMode()
} else {
val tableBlock = views.find { it.id == tableId } as BlockView.Table
val selectedCells = tableBlock.cells.mapNotNull {
if (currentSelection().contains(it.getId())) it else null
}
viewModelScope.launch {
orchestrator.stores.views.update(
views.updateTableBlockSelection(
tableId = tableBlock.id,
selection = currentSelection().toList()
)
)
renderCommand.send(Unit)
}
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.SimpleTableWidget.Show(
cellItems = listOf(cell).getSimpleTableWidgetItems(),
rowItems = emptyList(),
columnItems = emptyList(),
cells = selectedCells,
tableId = cell.tableId
)
)
}
}
private fun proceedWithSelectingCell(cellId: Id, tableId: Id) {

View file

@ -33,7 +33,6 @@ import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
import com.anytypeio.anytype.presentation.editor.editor.Orchestrator
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.template.EditorTemplateDelegate
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
@ -69,7 +68,6 @@ open class EditorViewModelFactory(
private val setDocCoverImage: SetDocCoverImage,
private val setDocImageIcon: SetDocumentImageIcon,
private val editorTemplateDelegate: EditorTemplateDelegate,
private val simpleTablesDelegate: SimpleTableDelegate,
private val createNewObject: CreateNewObject,
private val objectToSet: ConvertObjectToSet
) : ViewModelProvider.Factory {
@ -107,7 +105,6 @@ open class EditorViewModelFactory(
setDocImageIcon = setDocImageIcon,
templateDelegate = editorTemplateDelegate,
createNewObject = createNewObject,
simpleTableDelegate = simpleTablesDelegate,
objectToSet = objectToSet
) as T
}

View file

@ -169,6 +169,11 @@ sealed class Intent {
val targets: List<Id>,
val mark: Block.Content.Text.Mark
) : Text()
class ClearContent(
val context: Id,
val targets: List<Id>
): Text()
}
sealed class Media : Intent() {

View file

@ -5,6 +5,7 @@ import com.anytypeio.anytype.analytics.event.EventAnalytics
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.domain.base.suspendFold
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
import com.anytypeio.anytype.domain.block.interactor.DuplicateBlock
import com.anytypeio.anytype.domain.block.interactor.MergeBlocks
@ -88,7 +89,8 @@ class Orchestrator(
val stores: Editor.Storage,
val proxies: Editor.Proxer,
val textInteractor: Interactor.TextInteractor,
private val analytics: Analytics
private val analytics: Analytics,
private val clearBlockContent: ClearBlockContent
) {
private val defaultOnError: suspend (Throwable) -> Unit = { Timber.e(it) }
@ -627,6 +629,17 @@ class Orchestrator(
success = { payload -> proxies.payloads.send(payload) }
)
}
is Intent.Text.ClearContent -> {
clearBlockContent(
params = ClearBlockContent.Params(
ctx = intent.context,
blockIds = intent.targets
)
).process(
failure = defaultOnError,
success = { payload -> proxies.payloads.send(payload) }
)
}
}
}
}

View file

@ -1,8 +1,11 @@
package com.anytypeio.anytype.presentation.editor.editor.control
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashWidgetState
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetItem
import com.anytypeio.anytype.presentation.editor.markup.MarkupStyleDescriptor
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
@ -26,7 +29,9 @@ data class ControlPanelState(
val mentionToolbar: Toolbar.MentionToolbar = Toolbar.MentionToolbar.reset(),
val slashWidget: Toolbar.SlashWidget = Toolbar.SlashWidget.reset(),
val searchToolbar: Toolbar.SearchToolbar = Toolbar.SearchToolbar.reset(),
val objectTypesToolbar: Toolbar.ObjectTypes = Toolbar.ObjectTypes.reset()
val objectTypesToolbar: Toolbar.ObjectTypes = Toolbar.ObjectTypes.reset(),
val simpleTableWidget: Toolbar.SimpleTableWidget = Toolbar.SimpleTableWidget.reset(),
val cellsSelectTopWidget: Toolbar.CellSelection = Toolbar.CellSelection.reset()
) {
sealed class Toolbar {
@ -251,6 +256,38 @@ data class ControlPanelState(
)
}
}
data class SimpleTableWidget(
override val isVisible: Boolean,
val tableId: Id,
val cells: List<BlockView.Table.Cell>,
val cellItems: List<SimpleTableWidgetItem> = emptyList(),
val rowItems: List<SimpleTableWidgetItem> = emptyList(),
val columnItems: List<SimpleTableWidgetItem> = emptyList()
) : Toolbar() {
companion object {
fun reset(): SimpleTableWidget = SimpleTableWidget(
isVisible = false,
tableId = "",
cells = emptyList(),
cellItems = emptyList(),
rowItems = emptyList(),
columnItems = emptyList()
)
}
}
data class CellSelection(
override val isVisible: Boolean,
val count: Int
) : Toolbar() {
companion object {
fun reset(): CellSelection = CellSelection(
isVisible = false,
count = 0
)
}
}
}
/**
@ -300,7 +337,9 @@ data class ControlPanelState(
isVisible = false
),
slashWidget = Toolbar.SlashWidget.reset(),
objectTypesToolbar = Toolbar.ObjectTypes.reset()
objectTypesToolbar = Toolbar.ObjectTypes.reset(),
simpleTableWidget = Toolbar.SimpleTableWidget.reset(),
cellsSelectTopWidget = Toolbar.CellSelection.reset()
)
}
}

View file

@ -72,8 +72,6 @@ sealed interface ListenerType {
data class TableOfContentsItem(val target: Id, val item: Id) : ListenerType
data class TableOfContents(val target: Id) : ListenerType
data class TableEmptyCell(val cellId: Id, val rowId: Id, val tableId: Id) : ListenerType
data class TableTextCell(val cellId: Id, val tableId: Id) : ListenerType
data class TableEmptyCellMenu(val rowId: Id, val columnId: Id) : ListenerType
data class TableTextCellMenu(val cellId: Id, val rowId: Id, val tableId: Id) : ListenerType
data class TableEmptyCell(val cell: BlockView.Table.Cell) : ListenerType
data class TableTextCell(val cell: BlockView.Table.Cell) : ListenerType
}

View file

@ -192,6 +192,7 @@ sealed class BlockView : ViewType {
object H2 : Header()
object H3 : Header()
}
object Code : Style()
object Card : Style()
}
@ -739,7 +740,8 @@ sealed class BlockView : ViewType {
override val indent: Int = 0,
override val decorations: List<Decoration> = emptyList(),
val lang: String? = null
) : BlockView(), Permission, Selectable, Focusable, Cursor, Indentable, TextSupport, Decoratable {
) : BlockView(), Permission, Selectable, Focusable, Cursor, Indentable, TextSupport,
Decoratable {
override fun getViewType() = HOLDER_CODE_SNIPPET
}
@ -1090,7 +1092,7 @@ sealed class BlockView : ViewType {
override fun getViewType() = HOLDER_OBJECT_LINK_DEFAULT
}
sealed class Card: Default() {
sealed class Card : Default() {
abstract val isPreviousBlockMedia: Boolean
@ -1138,7 +1140,7 @@ sealed class BlockView : ViewType {
override val decorations: List<Decoration> = emptyList(),
override val objectTypeName: String? = null,
override val isPreviousBlockMedia: Boolean,
val cover : Cover?
val cover: Cover?
) : Card() {
override fun getViewType() = HOLDER_OBJECT_LINK_CARD_SMALL_ICON_COVER
}
@ -1155,7 +1157,7 @@ sealed class BlockView : ViewType {
override val decorations: List<Decoration> = emptyList(),
override val objectTypeName: String? = null,
override val isPreviousBlockMedia: Boolean,
val cover : Cover?
val cover: Cover?
) : Card() {
override fun getViewType() = HOLDER_OBJECT_LINK_CARD_MEDIUM_ICON_COVER
}
@ -1239,7 +1241,7 @@ sealed class BlockView : ViewType {
data class FeaturedRelation(
override val id: String,
val relations: List<DocumentRelationView>,
val allowChangingObjectType : Boolean = true
val allowChangingObjectType: Boolean = true
) : BlockView() {
override fun getViewType(): Int = HOLDER_FEATURED_RELATION
}
@ -1320,7 +1322,8 @@ sealed class BlockView : ViewType {
val background: ThemeColor = ThemeColor.DEFAULT,
val columns: List<Column>,
val cells: List<Cell>,
val rowCount: Int
val rowCount: Int,
val selectedCellsIds: List<Id>
) : BlockView(), Selectable {
override fun getViewType(): Int = HOLDER_TABLE
@ -1332,13 +1335,27 @@ sealed class BlockView : ViewType {
val columnId: Id,
val columnIndex: ColumnIndex,
val isHeader: Boolean = false,
val block: Text.Paragraph?
val block: Text.Paragraph?,
val tableId: Id,
val cellIndex: Int
) {
fun getId() = "$rowId-$columnId"
}
data class CellSelection(
val cellId: String,
val rowIndex: RowIndex,
val columnIndex: ColumnIndex,
var left: Boolean,
var top: Boolean,
var right: Boolean,
var bottom: Boolean,
var cellIndex: Int
)
@JvmInline
value class RowIndex(val value: Int)
@JvmInline
value class ColumnIndex(val value: Int)
}

View file

@ -1,63 +0,0 @@
package com.anytypeio.anytype.presentation.editor.editor.table
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.scan
import timber.log.Timber
interface SimpleTableDelegate {
val simpleTableDelegateState: Flow<SimpleTableWidgetState>
suspend fun onSimpleTableEvent(event: SimpleTableWidgetEvent)
}
class DefaultSimpleTableDelegate : SimpleTableDelegate {
private val events = MutableSharedFlow<SimpleTableWidgetEvent>(replay = 0)
override val simpleTableDelegateState =
events.scan(SimpleTableWidgetState.init()) { state, event ->
when (event) {
is SimpleTableWidgetEvent.onStart -> {
SimpleTableWidgetState.UpdateItems(
cellItems = listOf(
SimpleTableWidgetItem.Cell.ClearContents,
SimpleTableWidgetItem.Cell.Style,
SimpleTableWidgetItem.Cell.Color,
SimpleTableWidgetItem.Cell.ClearStyle
),
rowItems = listOf(
SimpleTableWidgetItem.Row.ClearContents,
SimpleTableWidgetItem.Row.Color,
SimpleTableWidgetItem.Row.Style,
SimpleTableWidgetItem.Row.Delete,
SimpleTableWidgetItem.Row.MoveUp,
SimpleTableWidgetItem.Row.MoveDown,
SimpleTableWidgetItem.Row.InsertAbove,
SimpleTableWidgetItem.Row.InsertBelow,
SimpleTableWidgetItem.Row.Duplicate,
SimpleTableWidgetItem.Row.Sort
),
columnItems = listOf(
SimpleTableWidgetItem.Column.ClearContents,
SimpleTableWidgetItem.Column.Color,
SimpleTableWidgetItem.Column.Style,
SimpleTableWidgetItem.Column.Delete,
SimpleTableWidgetItem.Column.InsertLeft,
SimpleTableWidgetItem.Column.InsertRight,
SimpleTableWidgetItem.Column.MoveLeft,
SimpleTableWidgetItem.Column.MoveRight,
SimpleTableWidgetItem.Column.Sort,
SimpleTableWidgetItem.Column.Duplicate
)
)
}
}
}.catch { e ->
Timber.e(e, "Error while processing simple table ")
}
override suspend fun onSimpleTableEvent(event: SimpleTableWidgetEvent) {
events.emit(event)
}
}

View file

@ -1,7 +0,0 @@
package com.anytypeio.anytype.presentation.editor.editor.table
import com.anytypeio.anytype.core_models.Id
sealed interface SimpleTableWidgetEvent {
data class onStart(val id: Id) : SimpleTableWidgetEvent
}

View file

@ -1,24 +0,0 @@
package com.anytypeio.anytype.presentation.editor.editor.table
sealed class SimpleTableWidgetState {
object Idle : SimpleTableWidgetState()
data class UpdateItems(
val cellItems: List<SimpleTableWidgetItem>,
val columnItems: List<SimpleTableWidgetItem>,
val rowItems: List<SimpleTableWidgetItem>
) : SimpleTableWidgetState() {
companion object {
fun empty() = UpdateItems(
cellItems = emptyList(),
columnItems = emptyList(),
rowItems = emptyList()
)
}
}
companion object {
fun init(): SimpleTableWidgetState = Idle
}
}

View file

@ -1,6 +0,0 @@
package com.anytypeio.anytype.presentation.editor.editor.table
sealed class SimpleTableWidgetViewState {
object Idle : SimpleTableWidgetViewState()
data class Active(val state: SimpleTableWidgetState) : SimpleTableWidgetViewState()
}

View file

@ -1994,7 +1994,8 @@ class DefaultBlockViewRenderer @Inject constructor(
selection = selection,
rows = rows,
columns = columns,
blocks = blocks
blocks = blocks,
tableId = block.id
)
}
}
@ -2008,11 +2009,13 @@ class DefaultBlockViewRenderer @Inject constructor(
block = block,
selection = selection
),
background = block.parseThemeBackgroundColor()
background = block.parseThemeBackgroundColor(),
selectedCellsIds = selection.toList()
)
}
private fun tableCells(
tableId: Id,
blocks: Map<String, List<Block>>,
rows: List<Block>,
columns: List<BlockView.Table.Column>,
@ -2051,6 +2054,7 @@ class DefaultBlockViewRenderer @Inject constructor(
} else {
null
}
val cellIndex = columnIndex*rows.size + rowIndex
cells.add(
BlockView.Table.Cell(
rowId = row.id,
@ -2058,7 +2062,9 @@ class DefaultBlockViewRenderer @Inject constructor(
columnId = column.id,
columnIndex = BlockView.Table.ColumnIndex(columnIndex),
isHeader = isHeader,
block = paragraph
tableId = tableId,
block = paragraph,
cellIndex = cellIndex
)
)
}

View file

@ -0,0 +1,378 @@
package com.anytypeio.anytype.presentation.editor.selection
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableWidgetItem
fun updateTableCellsSelectionState(
cellId: Id,
rowIndex: BlockView.Table.RowIndex,
columnIndex: BlockView.Table.ColumnIndex,
selectionState: List<BlockView.Table.CellSelection>,
cellIndex: Int
): List<BlockView.Table.CellSelection> {
val latestSelection = BlockView.Table.CellSelection(
cellId = cellId,
rowIndex = rowIndex,
columnIndex = columnIndex,
left = true,
top = true,
right = true,
bottom = true,
cellIndex = cellIndex
)
val latestSelectionIndex = selectionState.indexOfFirst { it.cellId == cellId }
return if (latestSelectionIndex != -1) {
//Latest selection is already in selection state - move on to deselecting a cell
val newState = selectionState.toMutableList()
newState.removeAt(latestSelectionIndex)
newState.restoreBordersVisibility(deselectedSelection = latestSelection)
newState
} else {
//Latest selection is not in selection state - move on to adding new selection to state
selectionState.forEach { selection ->
if (isLeftBorderMatch(cell1 = selection, cell2 = latestSelection)) {
selection.left = false
latestSelection.right = false
}
if (isRightBorderMatch(cell1 = selection, cell2 = latestSelection)) {
selection.right = false
latestSelection.left = false
}
if (isTopBorderMatch(cell1 = selection, cell2 = latestSelection)) {
selection.top = false
latestSelection.bottom = false
}
if (isBottomBorderMatch(cell1 = selection, cell2 = latestSelection)) {
selection.bottom = false
latestSelection.top = false
}
}
val newState = mutableListOf<BlockView.Table.CellSelection>().apply {
addAll(selectionState)
add(latestSelection)
}
newState
}
}
/**
* In the case of deselecting the cell it is necessary
* to restore the borders of the remaining selected cells
*/
fun List<BlockView.Table.CellSelection>.restoreBordersVisibility(deselectedSelection: BlockView.Table.CellSelection): List<BlockView.Table.CellSelection> =
map { cellSelection ->
if (isLeftBorderMatch(cell1 = cellSelection, cell2 = deselectedSelection)) {
cellSelection.left = true
}
if (isRightBorderMatch(cell1 = cellSelection, cell2 = deselectedSelection)) {
cellSelection.right = true
}
if (isTopBorderMatch(cell1 = cellSelection, cell2 = deselectedSelection)) {
cellSelection.top = true
}
if (isBottomBorderMatch(cell1 = cellSelection, cell2 = deselectedSelection)) {
cellSelection.bottom = true
}
cellSelection
}
/**
* Check that the left border of the first cell coincides with the right border of the second cell
* |cell2|cell1|
*/
fun isLeftBorderMatch(
cell1: BlockView.Table.CellSelection,
cell2: BlockView.Table.CellSelection
): Boolean {
val isSameRow = cell1.rowIndex.value == cell2.rowIndex.value
if (!isSameRow) return false
return cell1.columnIndex.value == (cell2.columnIndex.value + 1)
}
/**
* Check that the right border of the first cell coincides with the left border of the second cell
* |cell1|cell2|
*/
fun isRightBorderMatch(
cell1: BlockView.Table.CellSelection,
cell2: BlockView.Table.CellSelection
): Boolean {
val isSameRow = cell1.rowIndex.value == cell2.rowIndex.value
if (!isSameRow) return false
return cell2.columnIndex.value == (cell1.columnIndex.value + 1)
}
/**
* Check that the bottom border of the first cell coincides with the top border of the second cell
* cell1
* ------
* cell2
*/
fun isBottomBorderMatch(
cell1: BlockView.Table.CellSelection,
cell2: BlockView.Table.CellSelection
): Boolean {
val isSameColumn = cell1.columnIndex.value == cell2.columnIndex.value
if (!isSameColumn) return false
return cell1.rowIndex.value + 1 == cell2.rowIndex.value
}
/**
* Check that the top border of the first cell coincides with the top bottom of the second cell
* cell2
* ------
* cell1
*/
fun isTopBorderMatch(
cell1: BlockView.Table.CellSelection,
cell2: BlockView.Table.CellSelection
): Boolean {
val isSameColumn = cell1.columnIndex.value == cell2.columnIndex.value
if (!isSameColumn) return false
return cell1.rowIndex.value == cell2.rowIndex.value + 1
}
fun List<BlockView>.toggleTableMode(
cellsMode: BlockView.Mode,
selectedCellsIds: List<Id>
): List<BlockView> {
return map { view ->
when (view) {
is BlockView.Text.Paragraph -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Checkbox -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Bulleted -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Numbered -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Highlight -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Callout -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Header.One -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Header.Two -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Header.Three -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Text.Toggle -> view.copy(
mode = cellsMode,
isSelected = false,
isFocused = false,
cursor = null
)
is BlockView.Code -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Error.File -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Error.Video -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Error.Picture -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Error.Bookmark -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Upload.File -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Upload.Video -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Upload.Picture -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.MediaPlaceholder.File -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.MediaPlaceholder.Video -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.MediaPlaceholder.Bookmark -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.MediaPlaceholder.Picture -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Media.File -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Media.Video -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Media.Bookmark -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Media.Picture -> view.copy(
mode = cellsMode,
isSelected = false
)
is BlockView.Title.Basic -> view.copy(
mode = cellsMode
)
is BlockView.Title.Profile -> view.copy(
mode = cellsMode
)
is BlockView.Title.Todo -> view.copy(
mode = cellsMode
)
is BlockView.Title.Archive -> view.copy(
mode = cellsMode
)
is BlockView.Description -> view.copy(
mode = cellsMode
)
is BlockView.Relation.Placeholder -> view.copy(
isSelected = false
)
is BlockView.Relation.Related -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Default.Text -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Default.Card.SmallIcon -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Default.Card.MediumIcon -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Default.Card.SmallIconCover -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Default.Card.MediumIconCover -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Archived -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Deleted -> view.copy(
isSelected = false
)
is BlockView.LinkToObject.Loading -> view.copy(
isSelected = false
)
is BlockView.DividerDots -> view.copy(
isSelected = false
)
is BlockView.DividerLine -> view.copy(
isSelected = false
)
is BlockView.Latex -> view.copy(
isSelected = false
)
is BlockView.TableOfContents -> view.copy(
isSelected = false
)
is BlockView.Table -> {
view.copy(
isSelected = false,
cells = view.cells.updateCellsMode(mode = cellsMode),
selectedCellsIds = selectedCellsIds
)
}
is BlockView.FeaturedRelation -> view
is BlockView.Unsupported -> view.copy(isSelected = false)
is BlockView.Upload.Bookmark -> view.copy(isSelected = false)
}
}
}
fun List<BlockView.Table.Cell>.updateCellsMode(
mode: BlockView.Mode
): List<BlockView.Table.Cell> = map { cell ->
val block = cell.block
if (block == null) {
cell
} else {
cell.copy(
block = block.copy(
mode = mode,
isSelected = false,
isFocused = false,
cursor = null
)
)
}
}
fun List<BlockView>.updateTableBlockSelection(tableId: Id, selection: List<Id>): List<BlockView> =
map {
if (it.id == tableId && it is BlockView.Table) {
it.copy(
selectedCellsIds = selection
)
} else {
it
}
}
fun List<BlockView.Table.Cell>.getSimpleTableWidgetItems(): List<SimpleTableWidgetItem> {
return listOf(
SimpleTableWidgetItem.Cell.ClearContents
// SimpleTableWidgetItem.Cell.Style,
// SimpleTableWidgetItem.Cell.Color,
// SimpleTableWidgetItem.Cell.ClearStyle
)
}

View file

@ -0,0 +1,27 @@
package com.anytypeio.anytype.presentation.editor.selection
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class TableCellsSelectionState {
private var memory = listOf<BlockView.Table.CellSelection>()
fun set(cells: List<BlockView.Table.Cell>) {
cells.forEach { cell ->
val currentSelection = current()
memory = updateTableCellsSelectionState(
cellId = cell.getId(),
rowIndex = cell.rowIndex,
columnIndex = cell.columnIndex,
selectionState = currentSelection,
cellIndex = cell.cellIndex
)
}
}
fun clear() {
memory = emptyList()
}
fun current(): List<BlockView.Table.CellSelection> = memory
}

View file

@ -27,6 +27,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
import com.anytypeio.anytype.domain.block.interactor.DuplicateBlock
import com.anytypeio.anytype.domain.block.interactor.MergeBlocks
@ -101,8 +102,6 @@ import com.anytypeio.anytype.presentation.editor.editor.pattern.DefaultPatternMa
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashItem
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import com.anytypeio.anytype.presentation.editor.editor.table.DefaultSimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.render.parseThemeBackgroundColor
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
@ -327,8 +326,6 @@ open class EditorViewModelTest {
private lateinit var editorTemplateDelegate: EditorTemplateDelegate
private lateinit var simpleTableDelegate: SimpleTableDelegate
@Mock
lateinit var createNewObject: CreateNewObject
@ -344,6 +341,7 @@ open class EditorViewModelTest {
private lateinit var setDocCoverImage: SetDocCoverImage
private lateinit var setDocImageIcon: SetDocumentImageIcon
private lateinit var objectToSet: ConvertObjectToSet
private lateinit var clearBlockContent: ClearBlockContent
val root = MockDataFactory.randomUuid()
@ -377,7 +375,6 @@ open class EditorViewModelTest {
getTemplates = getTemplates,
applyTemplate = applyTemplate
)
simpleTableDelegate = DefaultSimpleTableDelegate()
}
@Test
@ -3942,6 +3939,7 @@ open class EditorViewModelTest {
setDocCoverImage = SetDocCoverImage(repo)
setDocImageIcon = SetDocumentImageIcon(repo)
downloadUnsplashImage = DownloadUnsplashImage(unsplashRepo)
clearBlockContent = ClearBlockContent(repo)
vm = EditorViewModel(
openPage = openPage,
@ -4000,7 +3998,8 @@ open class EditorViewModelTest {
updateBlocksMark = updateBlocksMark,
setObjectType = setObjectType,
createTable = createTable,
fillTableRow = fillTableRow
fillTableRow = fillTableRow,
clearBlockContent = clearBlockContent
),
analytics = analytics,
dispatcher = Dispatcher.Default(),
@ -4018,7 +4017,6 @@ open class EditorViewModelTest {
setDocCoverImage = setDocCoverImage,
setDocImageIcon = setDocImageIcon,
templateDelegate = editorTemplateDelegate,
simpleTableDelegate = simpleTableDelegate,
createNewObject = createNewObject,
objectToSet = objectToSet
)

View file

@ -15,6 +15,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.UpdateDivider
import com.anytypeio.anytype.domain.block.interactor.ClearBlockContent
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
import com.anytypeio.anytype.domain.block.interactor.DuplicateBlock
import com.anytypeio.anytype.domain.block.interactor.MergeBlocks
@ -75,8 +76,6 @@ import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.editor.editor.pattern.DefaultPatternMatcher
import com.anytypeio.anytype.presentation.editor.editor.table.DefaultSimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.editor.table.SimpleTableDelegate
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
import com.anytypeio.anytype.presentation.editor.template.DefaultEditorTemplateDelegate
@ -265,8 +264,6 @@ open class EditorPresentationTestSetup {
@Mock
lateinit var fillTableRow: FillTableRow
lateinit var simpleTableDelegate: SimpleTableDelegate
lateinit var editorTemplateDelegate: EditorTemplateDelegate
protected val builder: UrlBuilder get() = UrlBuilder(gateway)
@ -276,6 +273,7 @@ open class EditorPresentationTestSetup {
private lateinit var setDocCoverImage: SetDocCoverImage
private lateinit var setDocImageIcon: SetDocumentImageIcon
private lateinit var objectToSet: ConvertObjectToSet
private lateinit var clearBlockContent: ClearBlockContent
open lateinit var orchestrator: Orchestrator
@ -293,11 +291,11 @@ open class EditorPresentationTestSetup {
setDocCoverImage = SetDocCoverImage(repo)
setDocImageIcon = SetDocumentImageIcon(repo)
downloadUnsplashImage = DownloadUnsplashImage(unsplashRepo)
simpleTableDelegate = DefaultSimpleTableDelegate()
editorTemplateDelegate = DefaultEditorTemplateDelegate(
getTemplates = getTemplates,
applyTemplate = applyTemplate
)
clearBlockContent = ClearBlockContent(repo)
orchestrator = Orchestrator(
createBlock = createBlock,
@ -339,7 +337,8 @@ open class EditorPresentationTestSetup {
updateBlocksMark = updateBlocksMark,
setObjectType = setObjectType,
createTable = createTable,
fillTableRow = fillTableRow
fillTableRow = fillTableRow,
clearBlockContent = clearBlockContent
)
return EditorViewModel(
@ -376,7 +375,6 @@ open class EditorPresentationTestSetup {
setDocCoverImage = setDocCoverImage,
setDocImageIcon = setDocImageIcon,
templateDelegate = editorTemplateDelegate,
simpleTableDelegate = simpleTableDelegate,
createNewObject = createNewObject,
objectToSet = objectToSet
)

View file

@ -0,0 +1,369 @@
package com.anytypeio.anytype.presentation.editor.editor.ext
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Table.CellSelection
import com.anytypeio.anytype.presentation.editor.selection.TableCellsSelectionState
import com.anytypeio.anytype.test_utils.MockDataFactory
import org.junit.Test
import kotlin.test.assertEquals
class TableCellsSelectionStateTest {
val tableId = MockDataFactory.randomUuid()
@Test
fun `when click on cell expecting selected state with this cell`() {
//SETUP
val cellsSelectionState = TableCellsSelectionState()
//TESTING
//click on cell row1, column1
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row1",
columnId = "column1",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1),
block = null,
cellIndex = 22,
tableId = tableId
)
)
)
val actual = cellsSelectionState.current()
//EXPECTED
val expected = listOf(
CellSelection(
cellId = "row1-column1",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1),
left = true,
top = true,
right = true,
bottom = true,
cellIndex = 22
)
)
//ASSERT
assertEquals(expected, actual)
}
@Test
fun `when clicking on the same sell expecting empty state`() {
//SETUP
val cellsSelectionState = TableCellsSelectionState()
//TESTING
//click on cell row1, column1
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row1",
columnId = "column1",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1),
block = null,
tableId = tableId,
cellIndex = 11
)
)
)
//second click on cell row1, column1
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row1",
columnId = "column1",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1),
block = null,
cellIndex = 11,
tableId = tableId
)
)
)
val actual = cellsSelectionState.current()
//EXPECTED
val expected = emptyList<CellSelection>()
//ASSERT
assertEquals(expected, actual)
}
@Test
fun `when clicking on different cells expecting proper borders visibility`() {
//SETUP
val cellsSelectionState = TableCellsSelectionState()
//TESTING
//click on cell row1, column0
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row1",
columnId = "column0",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null,
cellIndex = 1,
tableId = tableId
)
)
)
//click on cell row1, column2
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row1",
columnId = "column2",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2),
block = null,
cellIndex = 7,
tableId = tableId
)
)
)
//click on cell row2, column0
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row2",
columnId = "column0",
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null,
cellIndex = 2,
tableId = tableId
)
)
)
//click on cell row0, column2
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row0",
columnId = "column2",
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2),
block = null,
cellIndex = 6,
tableId = tableId
)
)
)
//click on cell row1, column1
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row1",
columnId = "column1",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1),
block = null,
cellIndex = 4,
tableId = tableId
)
)
)
val actual = cellsSelectionState.current()
//EXPECTED
val expected = listOf(
CellSelection(
cellId = "row1-column0",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0),
left = true,
top = true,
right = false,
bottom = false,
cellIndex = 1
),
CellSelection(
cellId = "row1-column2",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2),
left = false,
top = false,
right = true,
bottom = true,
cellIndex = 7
),
CellSelection(
cellId = "row2-column0",
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(0),
left = true,
top = false,
right = true,
bottom = true,
cellIndex = 2
),
CellSelection(
cellId = "row0-column2",
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2),
left = true,
top = true,
right = true,
bottom = false,
cellIndex = 6
),
CellSelection(
cellId = "row1-column1",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1),
left = false,
top = true,
right = false,
bottom = true,
cellIndex = 4
)
)
//ASSERT
assertEquals(5, actual.size, message = "Size of selected cells expected 5")
assertEquals(expected, actual)
//TESTING
//click on cell row1, column1
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row1",
columnId = "column1",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1),
block = null,
cellIndex = 4,
tableId = tableId
)
)
)
val actual2 = cellsSelectionState.current()
//EXPECTED
val expected2 = listOf(
CellSelection(
cellId = "row1-column0",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0),
left = true,
top = true,
right = true,
bottom = false,
cellIndex = 1
),
CellSelection(
cellId = "row1-column2",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2),
left = true,
top = false,
right = true,
bottom = true,
cellIndex = 7
),
CellSelection(
cellId = "row2-column0",
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(0),
left = true,
top = false,
right = true,
bottom = true,
cellIndex = 2
),
CellSelection(
cellId = "row0-column2",
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2),
left = true,
top = true,
right = true,
bottom = false,
cellIndex = 6
)
)
//ASSERT
assertEquals(4, actual2.size, message = "Size of selected cells expected 4")
assertEquals(expected2, actual2)
//TESTING
//click on cell row0, column2
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row0",
columnId = "column2",
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2),
block = null,
cellIndex = 6,
tableId = tableId
)
)
)
//click on cell row0, column2
cellsSelectionState.set(
cells = listOf(
BlockView.Table.Cell(
rowId = "row2",
columnId = "column0",
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null,
cellIndex = 2,
tableId = tableId
)
)
)
val actual3 = cellsSelectionState.current()
//EXPECTED
val expected3 = listOf(
CellSelection(
cellId = "row1-column0",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0),
left = true,
top = true,
right = true,
bottom = true,
cellIndex = 1
),
CellSelection(
cellId = "row1-column2",
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2),
left = true,
top = true,
right = true,
bottom = true,
cellIndex = 7
)
)
//ASSERT
assertEquals(2, actual3.size, message = "Size of selected cells expected 2")
assertEquals(expected3, actual3)
}
}

View file

@ -4,15 +4,18 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubLayoutColumns
import com.anytypeio.anytype.core_models.StubLayoutRows
import com.anytypeio.anytype.core_models.StubParagraph
import com.anytypeio.anytype.core_models.StubSmartBlock
import com.anytypeio.anytype.core_models.StubTable
import com.anytypeio.anytype.core_models.StubTableCells
import com.anytypeio.anytype.core_models.StubTableColumns
import com.anytypeio.anytype.core_models.StubTableRows
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.domain.table.FillTableRow
import com.anytypeio.anytype.presentation.editor.editor.EditorPresentationTestSetup
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import kotlinx.coroutines.runBlocking
import org.junit.Before
@ -41,7 +44,10 @@ class EditorTableBlockTest : EditorPresentationTestSetup() {
val columns = StubTableColumns(size = 3)
val rows = StubTableRows(size = 2)
val cells = StubTableCells(columns = listOf(), rows = listOf())
val cells = StubTableCells(
columns = listOf(columns[0], columns[1], columns[2]),
rows = listOf(rows[0], rows[1])
)
val columnLayout = StubLayoutColumns(children = columns.map { it.id })
val rowLayout = StubLayoutRows(children = rows.map { it.id })
val table = StubTable(children = listOf(columnLayout.id, rowLayout.id))
@ -62,24 +68,33 @@ class EditorTableBlockTest : EditorPresentationTestSetup() {
val vm = buildViewModel()
val cell1Id = "${rows[0].id}-${columns[1].id}"
val cell2Id = "${rows[1].id}-${columns[0].id}"
vm.onStart(root)
vm.apply {
onClickListener(
ListenerType.TableEmptyCell(
cellId = cell1Id,
rowId = rows[0].id,
tableId = table.id
cell = BlockView.Table.Cell(
rowId = rows[0].id,
columnId = columns[1].id,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1),
block = null,
cellIndex = 0,
tableId = table.id
)
)
)
onClickListener(
ListenerType.TableEmptyCell(
cellId = cell2Id,
rowId = rows[1].id,
tableId = table.id
cell = BlockView.Table.Cell(
rowId = rows[1].id,
columnId = columns[0].id,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null,
cellIndex = 1,
tableId = table.id
)
)
)
}
@ -98,7 +113,7 @@ class EditorTableBlockTest : EditorPresentationTestSetup() {
}
@Test
fun `should amend second text cell click`() {
fun `should not amend second text cell click`() {
val columns = StubTableColumns(size = 3)
val rows = StubTableRows(size = 2)
@ -121,22 +136,39 @@ class EditorTableBlockTest : EditorPresentationTestSetup() {
stubOpenDocument(document)
val vm = buildViewModel()
val cell1Id = "${rows[0].id}-${columns[1].id}"
val cell2Id = "${rows[1].id}-${columns[0].id}"
vm.onStart(root)
vm.apply {
onClickListener(
ListenerType.TableTextCell(
cellId = cell1Id,
tableId = table.id
cell = BlockView.Table.Cell(
rowId = rows[0].id,
columnId = columns[1].id,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1),
block = BlockView.Text.Paragraph(
id = cells[0].id,
text = cells[0].content.asText().text
),
cellIndex = 0,
tableId = table.id
)
)
)
onClickListener(
ListenerType.TableTextCell(
cellId = cell2Id,
tableId = table.id
cell = BlockView.Table.Cell(
rowId = rows[1].id,
columnId = columns[0].id,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0),
block = BlockView.Text.Paragraph(
id = cells[1].id,
text = cells[1].content.asText().text
),
cellIndex = 1,
tableId = table.id
)
)
)
}
@ -144,7 +176,6 @@ class EditorTableBlockTest : EditorPresentationTestSetup() {
val selectedState = vm.currentSelection()
runBlocking {
assertEquals(1, selectedState.size)
assertEquals(cell1Id, selectedState.first())
verifyNoInteractions(fillTableRow)
}
}

View file

@ -43,7 +43,8 @@ class TableBlockRendererTest {
class BlockViewRenderWrapper(
private val blocks: Map<Id, List<Block>>,
private val renderer: BlockViewRenderer,
private val restrictions: List<ObjectRestriction> = emptyList()
private val restrictions: List<ObjectRestriction> = emptyList(),
private val selections: Set<Id> = emptySet()
) : BlockViewRenderer by renderer {
suspend fun render(
root: Block,
@ -59,7 +60,7 @@ class TableBlockRendererTest {
details = details,
relations = emptyList(),
restrictions = restrictions,
selection = emptySet(),
selection = selections,
objectTypes = listOf()
)
}
@ -199,7 +200,9 @@ class TableBlockRendererTest {
rowId = row.id,
columnId = column.id,
rowIndex = BlockView.Table.RowIndex(rowIndex),
columnIndex = BlockView.Table.ColumnIndex(columnIndex)
columnIndex = BlockView.Table.ColumnIndex(columnIndex),
cellIndex = columnIndex*rows.size + rowIndex,
tableId = table.id
)
)
}
@ -243,7 +246,8 @@ class TableBlockRendererTest {
cells = cells,
columns = columnViews,
rowCount = rowsSize,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
) + blocksDown.mapIndexed { idx, block ->
BlockView.Text.Numbered(
@ -351,7 +355,9 @@ class TableBlockRendererTest {
columnId = column.id,
rowIndex = BlockView.Table.RowIndex(rowIndex),
columnIndex = BlockView.Table.ColumnIndex(columnIndex),
block = null
block = null,
cellIndex = columnIndex*rows.size + rowIndex,
tableId = table.id
)
)
}
@ -395,7 +401,8 @@ class TableBlockRendererTest {
cells = cells,
columns = columnViews,
rowCount = rowsSize,
isSelected = false
isSelected = false,
selectedCellsIds = emptyList()
)
) + blocksDown.mapIndexed { idx, block ->
BlockView.Text.Numbered(
@ -499,7 +506,8 @@ class TableBlockRendererTest {
wrapper = BlockViewRenderWrapper(
blocks = map,
renderer = renderer
renderer = renderer,
selections = setOf("$rowId2-$columnId1")
)
val result = runBlocking {
@ -519,7 +527,9 @@ class TableBlockRendererTest {
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null
block = null,
cellIndex = 0,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId2,
@ -529,14 +539,18 @@ class TableBlockRendererTest {
text = row2Block1.content.asText().text
),
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(0)
columnIndex = BlockView.Table.ColumnIndex(0),
cellIndex = 1,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId3,
columnId = columnId1,
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(0),
block = null
block = null,
cellIndex = 2,
tableId = table.id
), //column1
BlockView.Table.Cell(
rowId = rowId1,
@ -546,7 +560,9 @@ class TableBlockRendererTest {
text = row1Block1.content.asText().text
),
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 3,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId2,
@ -556,35 +572,45 @@ class TableBlockRendererTest {
text = row2Block2.content.asText().text
),
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(1)
columnIndex = BlockView.Table.ColumnIndex(1),
cellIndex = 4,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId3,
columnId = columnId2,
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(1),
block = null
block = null,
cellIndex = 5,
tableId = table.id
),//column2
BlockView.Table.Cell(
rowId = rowId1,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(2),
block = null
block = null,
cellIndex = 6,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId2,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(2),
block = null
block = null,
cellIndex = 7,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId3,
columnId = columnId3,
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(2),
block = null
block = null,
cellIndex = 8,
tableId = table.id
),//column3
BlockView.Table.Cell(
rowId = rowId1,
@ -594,7 +620,9 @@ class TableBlockRendererTest {
text = row1Block2.content.asText().text
),
rowIndex = BlockView.Table.RowIndex(0),
columnIndex = BlockView.Table.ColumnIndex(3)
columnIndex = BlockView.Table.ColumnIndex(3),
cellIndex = 9,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId2,
@ -604,15 +632,19 @@ class TableBlockRendererTest {
text = row2Block3.content.asText().text
),
rowIndex = BlockView.Table.RowIndex(1),
columnIndex = BlockView.Table.ColumnIndex(3)
columnIndex = BlockView.Table.ColumnIndex(3),
cellIndex = 10,
tableId = table.id
),
BlockView.Table.Cell(
rowId = rowId3,
columnId = columnId4,
rowIndex = BlockView.Table.RowIndex(2),
columnIndex = BlockView.Table.ColumnIndex(3),
block = null
),
block = null,
cellIndex = 11,
tableId = table.id
)
)
val columnViews = mutableListOf<BlockView.Table.Column>()
@ -653,7 +685,8 @@ class TableBlockRendererTest {
cells = cells,
columns = columnViews,
rowCount = rowsSize,
isSelected = false
isSelected = false,
selectedCellsIds = listOf("$rowId2-$columnId1")
)
) + blocksDown.mapIndexed { idx, block ->
BlockView.Text.Numbered(