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

Merge branch 'feature_render_blocks_with_header_content_types' into 'develop'

Feature render blocks with header content types

See merge request ra3orbladez/anytype.io.mobile!7
This commit is contained in:
Evgenii Kozlov 2019-03-24 09:09:14 +00:00
commit 0394e6205e
17 changed files with 339 additions and 40 deletions

View file

@ -5,23 +5,31 @@
"parentId": "",
"type": 3,
"contentType": 3,
"content": "{\"text\":\"Заголовок\",\"marks\":[]}",
"content": "{\"text\":\"Заголовок (h1)\",\"marks\":[]}",
"children": []
},
{
"id": "02ca4410-2cff-5978-81e5-09dc76dd003c",
"parentId": "",
"type": 3,
"contentType": 1,
"content": "{\"text\":\"первый элемент\",\"marks\":[]}",
"contentType": 4,
"content": "{\"text\":\"первый элемент (h3)\",\"marks\":[]}",
"children": []
},
{
"id": "116271cc-c6e3-5c58-8598-9342567b9a66",
"parentId": "",
"type": 3,
"contentType": 1,
"content": "{\"text\":\"второй элемент\",\"marks\":[]}",
"contentType": 8,
"content": "{\"text\":\"Все счастливые семьи похожи друг на друга, каждая несчастливая семья несчастлива по-своему\",\"marks\":[]}",
"children": []
},
{
"id": "116271cc-c6e3-5c58-8598-9342567b9a66",
"parentId": "",
"type": 3,
"contentType": 10,
"content": "{\"text\":\"Написать письмо\",\"marks\":[]}",
"children": []
},
{
@ -35,8 +43,8 @@
"id": "22a8f9f5-b042-5532-bb5d-cbda07f579e0",
"parentId": "93f8810e-590f-5962-a662-2c1fe17e5cbe",
"type": 3,
"contentType": 1,
"content": "{\"text\":\"чайлд третьего\",\"marks\":[]}",
"contentType": 2,
"content": "{\"text\":\"data class User(val name : String)\",\"marks\":[]}",
"children": [
{
"id": "1983",
@ -62,8 +70,8 @@
"id": "21fe093a-5f74-583b-9c17-ee798e1bfc7e",
"parentId": "",
"type": 3,
"contentType": 1,
"content": "{\"text\":\"четвертый элемент\",\"marks\":[]}",
"contentType": 2,
"content": "{\"text\":\"data class User(val name : String)\",\"marks\":[]}",
"children": []
},
{

View file

@ -6,7 +6,7 @@ import com.agileburo.anytype.feature_editor.data.BlockConverter
import com.agileburo.anytype.feature_editor.data.BlockConverterImpl
import com.agileburo.anytype.feature_editor.data.EditorRepo
import com.agileburo.anytype.feature_editor.data.EditorRepoImpl
import com.agileburo.anytype.feature_editor.data.datasource.IPFSDataSource
import com.agileburo.anytype.feature_editor.data.datasource.BlockDataSource
import com.agileburo.anytype.feature_editor.data.datasource.IPFSDataSourceImpl
import com.agileburo.anytype.feature_editor.domain.EditorInteractor
import com.agileburo.anytype.feature_editor.domain.EditorInteractorImpl
@ -39,7 +39,7 @@ class EditorModule {
@Provides
@PerFeature
fun provideRepo(blockConverter: BlockConverter, dataSource: IPFSDataSource): EditorRepo =
fun provideRepo(blockConverter: BlockConverter, dataSource: BlockDataSource): EditorRepo =
EditorRepoImpl(dataSource = dataSource, blockConverter = blockConverter)
@Provides
@ -48,7 +48,7 @@ class EditorModule {
@Provides
@PerFeature
fun provideDataSource(context: Context, gson: Gson): IPFSDataSource =
fun provideDataSource(context: Context, gson: Gson): BlockDataSource =
IPFSDataSourceImpl(context = context, gson = gson)
@Provides

View file

@ -1,6 +1,8 @@
package com.agileburo.anytype.feature_editor.data
import com.agileburo.anytype.feature_editor.domain.Block
import com.agileburo.anytype.feature_editor.domain.toContentType
import com.agileburo.anytype.feature_editor.domain.toNumericalCode
/**
* Created by Konstantin Ivanov
@ -17,13 +19,15 @@ class BlockConverterImpl : BlockConverter {
override fun modelToDomain(model: BlockModel) = Block(
id = model.id,
content = model.content,
parentId = model.parentId
parentId = model.parentId,
contentType = model.contentType.toContentType()
)
override fun domainToModel(block: Block) = BlockModel(
id = block.id,
content = block.content,
parentId = block.parentId,
children = mutableListOf()
children = mutableListOf(),
contentType = block.contentType.toNumericalCode()
)
}

View file

@ -9,5 +9,6 @@ data class BlockModel(
val id: String,
val parentId: String = "",
val content: String = "",
val contentType : Int,
val children: List<BlockModel>
)

View file

@ -1,6 +1,6 @@
package com.agileburo.anytype.feature_editor.data
import com.agileburo.anytype.feature_editor.data.datasource.IPFSDataSource
import com.agileburo.anytype.feature_editor.data.datasource.BlockDataSource
import com.agileburo.anytype.feature_editor.domain.Block
import io.reactivex.Single
import javax.inject.Inject
@ -12,19 +12,19 @@ interface EditorRepo {
}
class EditorRepoImpl @Inject constructor(
private val dataSource: IPFSDataSource,
private val dataSource: BlockDataSource,
private val blockConverter: BlockConverter
) : EditorRepo {
override fun getBlocks(): Single<List<Block>> {
return dataSource.getBlocks()
.flatMap { t: List<BlockModel> -> Single.just(unwrap(t)) }
return dataSource.getBlocks().map(this::unwrap)
}
override fun saveState(list: List<Block>) {
wrap(list)
}
// TODO перевести в отдельный маппер, пусть репозиторий отвечает толька за CRUD-операции
private fun unwrap(blocks: List<BlockModel>): List<Block> {
val result = mutableListOf<Block>()
if (blocks.isEmpty()) return result

View file

@ -11,14 +11,14 @@ import javax.inject.Inject
* email : ki@agileburo.com
* on 20.03.2019.
*/
interface IPFSDataSource {
interface BlockDataSource {
fun getBlocks(): Single<List<BlockModel>>
}
class IPFSDataSourceImpl @Inject constructor(
private val context: Context,
private val gson: Gson
) : IPFSDataSource {
) : BlockDataSource {
override fun getBlocks(): Single<List<BlockModel>> {
return Single.create<List<BlockModel>> { emitter ->
@ -26,8 +26,11 @@ class IPFSDataSourceImpl @Inject constructor(
val json = context.assets.open("test.json").bufferedReader().use {
it.readText()
}
val ipfsResponse = gson.fromJson<IpfsResponse>(json, IpfsResponse::class.java)
emitter.onSuccess(ipfsResponse.blocks)
} catch (e: Exception) {
emitter.onError(e)
}

View file

@ -26,18 +26,19 @@ sealed class ContentType {
object H3 : ContentType()
object OL : ContentType()
object UL : ContentType()
object HL : ContentType()
object Quote : ContentType()
object Toggle : ContentType()
object Check : ContentType()
object H4 : ContentType()
}
data class Block(
val id: String = "",
val parentId: String = "",
//val type: BlockType = BlockType.Editable,
//val contentType: ContentType = ContentType.H1,
val content: String = ""
val id: String,
val parentId: String,
val contentType: ContentType,
// TODO parse marks and other stuff
val content: String
// TODO add blockType
)
fun Int.toContentType(): ContentType =
@ -49,13 +50,25 @@ fun Int.toContentType(): ContentType =
5 -> ContentType.H3
6 -> ContentType.OL
7 -> ContentType.UL
8 -> ContentType.HL
8 -> ContentType.Quote
9 -> ContentType.Toggle
10 -> ContentType.Check
11 -> ContentType.H4
else -> ContentType.H1
}
fun ContentType.toNumericalCode() : Int {
return when(this) {
ContentType.P -> 1
ContentType.Code -> 2
ContentType.H1 -> 3
ContentType.H2 -> 4
ContentType.Quote -> 8
ContentType.Check -> 10
else -> TODO()
}
}
fun Int.toBlockType(): BlockType =
when (this) {
1 -> BlockType.HrGrid

View file

@ -25,12 +25,10 @@ class EditorViewModel(private val interactor: EditorInteractor) : ViewModel() {
interactor.getBlocks()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({ blocks: List<Block> ->
progress.accept(EditorState.Result(blocks))
},
{ t: Throwable ->
Timber.d("Get blocks error : $t")
})
.subscribe(
{ blocks -> progress.accept(EditorState.Result(blocks)) },
{ error -> Timber.d("Get blocks error : $error") }
)
.disposedBy(disposable)
}

View file

@ -7,28 +7,85 @@ import androidx.recyclerview.widget.RecyclerView
import com.agileburo.anytype.core_utils.swap
import com.agileburo.anytype.feature_editor.R
import com.agileburo.anytype.feature_editor.domain.Block
import com.agileburo.anytype.feature_editor.domain.ContentType
import kotlinx.android.synthetic.main.item_block_checkbox.view.*
import kotlinx.android.synthetic.main.item_block_code_snippet.view.*
import kotlinx.android.synthetic.main.item_block_editable.view.*
import kotlinx.android.synthetic.main.item_block_header_one.view.*
import kotlinx.android.synthetic.main.item_block_header_three.view.*
import kotlinx.android.synthetic.main.item_block_header_two.view.*
import kotlinx.android.synthetic.main.item_block_quote.view.*
import timber.log.Timber
class EditorAdapter(private val blocks: MutableList<Block>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
fun setBlocks(blocks: List<Block>) {
Timber.d("Set blocks ${blocks.size}")
this.blocks.addAll(blocks)
fun setBlocks(items: List<Block>) {
Timber.d("Set blocks ${items.size}")
blocks.addAll(items)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.item_block_editable, parent, false)
return ViewHolder.TextHolder(view)
return when(viewType) {
HOLDER_PARAGRAPH -> {
val view = inflater.inflate(R.layout.item_block_editable, parent, false)
ViewHolder.ParagraphHolder(view)
}
HOLDER_HEADER_ONE -> {
val view = inflater.inflate(R.layout.item_block_header_one, parent, false)
ViewHolder.HeaderOneHolder(view)
}
HOLDER_HEADER_TWO -> {
val view = inflater.inflate(R.layout.item_block_header_two, parent, false)
ViewHolder.HeaderTwoHolder(view)
}
HOLDER_HEADER_THREE -> {
val view = inflater.inflate(R.layout.item_block_header_three, parent, false)
ViewHolder.HeaderThreeHolder(view)
}
HOLDER_QUOTE -> {
val view = inflater.inflate(R.layout.item_block_quote, parent, false)
ViewHolder.QuoteHolder(view)
}
HOLDER_CHECKBOX -> {
val view = inflater.inflate(R.layout.item_block_checkbox, parent, false)
ViewHolder.CheckBoxHolder(view)
}
HOLDER_CODE_SNIPPET -> {
val view = inflater.inflate(R.layout.item_block_code_snippet, parent, false)
ViewHolder.CodeSnippetHolder(view)
}
else -> TODO()
}
}
override fun getItemViewType(position: Int): Int {
return when(blocks[position].contentType) {
is ContentType.P -> HOLDER_PARAGRAPH
is ContentType.H1 -> HOLDER_HEADER_ONE
is ContentType.H2 -> HOLDER_HEADER_TWO
is ContentType.H3 -> HOLDER_HEADER_THREE
is ContentType.Quote -> HOLDER_QUOTE
is ContentType.Check -> HOLDER_CHECKBOX
is ContentType.Code -> HOLDER_CODE_SNIPPET
else -> TODO()
}
}
override fun getItemCount() = blocks.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ViewHolder.TextHolder -> holder.bind(blocks[position])
is ViewHolder.ParagraphHolder -> holder.bind(blocks[position])
is ViewHolder.HeaderOneHolder -> holder.bind(blocks[position])
is ViewHolder.HeaderTwoHolder -> holder.bind(blocks[position])
is ViewHolder.HeaderThreeHolder -> holder.bind(blocks[position])
is ViewHolder.QuoteHolder -> holder.bind(blocks[position])
is ViewHolder.CheckBoxHolder -> holder.bind(blocks[position])
is ViewHolder.CodeSnippetHolder -> holder.bind(blocks[position])
}
}
@ -44,7 +101,7 @@ class EditorAdapter(private val blocks: MutableList<Block>): RecyclerView.Adapte
sealed class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
class TextHolder(itemView: View) : ViewHolder(itemView) {
class ParagraphHolder(itemView: View) : ViewHolder(itemView) {
fun bind(block : Block) {
itemView.tvId.text = "id :${block.id}"
@ -52,5 +109,58 @@ class EditorAdapter(private val blocks: MutableList<Block>): RecyclerView.Adapte
}
}
class HeaderOneHolder(itemView: View) : ViewHolder(itemView) {
fun bind(block : Block) {
itemView.headerContentText.text = block.content
}
}
class HeaderTwoHolder(itemView: View) : ViewHolder(itemView) {
fun bind(block : Block) {
itemView.headerTwoContentText.text = block.content
}
}
class HeaderThreeHolder(itemView: View) : ViewHolder(itemView) {
fun bind(block : Block) {
itemView.headerThreeContentText.text = block.content
}
}
class QuoteHolder(itemView: View) : ViewHolder(itemView) {
fun bind(block : Block) {
itemView.quoteContent.text = block.content
}
}
class CheckBoxHolder(itemView: View) : ViewHolder(itemView) {
fun bind(block : Block) {
itemView.checkBoxContent.text = block.content
}
}
class CodeSnippetHolder(itemView: View) : ViewHolder(itemView) {
fun bind(block: Block) {
itemView.codeSnippetContent.text = block.content
}
}
}
companion object {
const val HOLDER_PARAGRAPH = 0
const val HOLDER_HEADER_ONE = 1
const val HOLDER_HEADER_TWO = 2
const val HOLDER_HEADER_THREE = 3
const val HOLDER_QUOTE = 4
const val HOLDER_CHECKBOX = 5
const val HOLDER_CODE_SNIPPET = 6
}
}

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/dark_gray"/>
<corners android:radius="4dp"/>
</shape>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/checkBoxContent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="12sp"
android:typeface="monospace"
android:textColor="#FFFF"
android:padding="8dp"
android:background="@drawable/rounded_rectangle_gray"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/codeSnippetContent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textStyle="bold"
android:textSize="24sp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/headerContentText"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textStyle="bold"
android:textSize="16sp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/headerThreeContentText"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textStyle="bold"
android:textSize="18sp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/headerTwoContentText"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:paddingTop="12dp"
android:paddingBottom="12dp"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:textStyle="bold"
android:textSize="14sp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/quoteContent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="32dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"/>
<View android:layout_width="1dp"
android:background="#808080"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#7a1ced</color>
<color name="dark_gray">#222936</color>
<color name="colorAccent">#FF4081</color>
</resources>