mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Feature/code snippet lang selection (#1042)
This commit is contained in:
parent
a46000d384
commit
54fe941d43
44 changed files with 678 additions and 83 deletions
|
@ -4,7 +4,7 @@
|
|||
"function": "#6f42c1",
|
||||
"number": "#0366d6",
|
||||
"boolean": "#0366d6",
|
||||
"operator": "#0366d6",
|
||||
"operator": "#d73a49",
|
||||
"string": "#05264c",
|
||||
"property": "#0366d6"
|
||||
}
|
38
app/src/main/assets/syntax/generic.json
Normal file
38
app/src/main/assets/syntax/generic.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "\\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\\b",
|
||||
"color": "#d73a49",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "(^|[^.])(?:<<=?|>>>?=?|->|--|\\+\\+|&&|\\|\\||::|[?:~]|[-+*/%&|^!=<>]=?)",
|
||||
"color": "#d73a49",
|
||||
"key": "operator"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "(^|[^\\\\])#.*",
|
||||
"color": "#6a737d",
|
||||
"key": "comment"
|
||||
},
|
||||
{
|
||||
"pattern": "(?:\\b(?=\\d)|\\B(?=\\.))(?:0[bo])?(?:(?:\\d|0x[\\da-f])[\\da-f]*\\.?\\d*|\\.\\d+)(?:e[+-]?\\d+)?j?\\b",
|
||||
"color": "#0366d6",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "(?:[rub]|rb|br)?(\"|')(?:\\\\.|(?!\\1)[^\\\\\\r\\n])*\\1",
|
||||
"color": "#05264c",
|
||||
"key": "string"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\/\\/).*",
|
||||
"color": "#6a737d",
|
||||
"key": "comment"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
"operators": [
|
||||
{
|
||||
"pattern": "[*\\/%^!=]=?|\\+[=+]?|-[=-]?|\\|[=|]?|&(?:=|&|\\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\\.\\.\\.",
|
||||
"color": "#0366d6",
|
||||
"color": "#d73a49",
|
||||
"key": "operator"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -6,12 +6,18 @@
|
|||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "(^|[^.])(?:<<=?|>>>?=?|->|--|\\+\\+|&&|\\|\\||::|[?:~]|[-+*/%&|^!=<>]=?)",
|
||||
"color": "#d73a49",
|
||||
"key": "operator"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "\\(|\\)|\\[|\\]|:|;|\\.|\\||;|\\&|\\{|\\}",
|
||||
"color": "#b71c1c",
|
||||
"key": ""
|
||||
"pattern": "\\w+(?=\\s*\\()",
|
||||
"color": "#6f42c1",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "@\\w+",
|
||||
|
@ -19,19 +25,24 @@
|
|||
"key": "annotation"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b\\d+[\\.]?\\d*([eE]\\-?\\d+)?[lLdDfF]?\\b|\\b0x[a-fA-F\\d]+\\b",
|
||||
"color": "#f4511e",
|
||||
"key:": ""
|
||||
"pattern": "\\b0b[01][01_]*L?\\b|\\b0x[\\da-f_]*\\.?[\\da-f_p+-]+\\b|(?:\\b\\d[\\d_]*\\.?[\\d_]*|\\B\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[dfl]?",
|
||||
"color": "#0366d6",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\\"(.*)\\\"|\\\"(.*)\\\")",
|
||||
"color": "#ff0000",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "\\b[A-Z](?:\\w*[a-z]\\w*)?\\b",
|
||||
"pattern": "\\b[A-Z]\\w*(?=\\s+\\w+\\s*[;,=())])",
|
||||
"color": "#6e5494",
|
||||
"key": "class"
|
||||
},
|
||||
{
|
||||
"pattern": "(?:[rub]|rb|br)?(\"|')(?:\\\\.|(?!\\1)[^\\\\\\r\\n])*\\1",
|
||||
"color": "#05264c",
|
||||
"key": "string"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\/\\/).*",
|
||||
"color": "#6a737d",
|
||||
"key": "comment"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,37 +1,48 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "\\w+(?=\\s*\\()",
|
||||
"color": "#6f42c1",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "(^|[^.])\\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\\b",
|
||||
"color": "#d73a49",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "\\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\\/*%<>]=?|[?:]:?|\\.\\.|&&|\\|\\||\\b(?:and|inv|or|shl|shr|ushr|xor)\\b",
|
||||
"color": "#d73a49",
|
||||
"key": "operator"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "@\\w+",
|
||||
"pattern": "\\w+(?=\\s*\\()",
|
||||
"color": "#6f42c1",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "\\B@(?:\\w+:)?(?:[A-Z]\\w*|\\[[^\\]]+\\])",
|
||||
"color": "#b87333",
|
||||
"key": "annotation"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b\\d+[\\.]?\\d*([eE]\\-?\\d+)?[lLdDfF]?\\b|\\b0x[a-fA-F\\d]+\\b",
|
||||
"pattern": "\\b(?:0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\\d+(?:_\\d+)*(?:\\.\\d+(?:_\\d+)*)?(?:[eE][+-]?\\d+(?:_\\d+)*)?[fFL]?)\\b",
|
||||
"color": "#0366d6",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\\"(.*)\\\"|\\\"(.*)\\\")",
|
||||
"color": "#ff0000",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "\\b[A-Z](?:\\w*[a-z]\\w*)?\\b",
|
||||
"color": "#6e5494",
|
||||
"key": "class"
|
||||
},
|
||||
{
|
||||
"pattern": "(?:[rub]|rb|br)?(\"|')(?:\\\\.|(?!\\1)[^\\\\\\r\\n])*\\1",
|
||||
"color": "#05264c",
|
||||
"key": "string"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\/\\/).*",
|
||||
"color": "#6a737d",
|
||||
"key": "comment"
|
||||
}
|
||||
]
|
||||
}
|
67
app/src/main/assets/syntax/languages.json
Normal file
67
app/src/main/assets/syntax/languages.json
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"abap": "ABAP",
|
||||
"arduino": "Arduino",
|
||||
"bash": "Bash",
|
||||
"basic": "BASIC",
|
||||
"c": "C",
|
||||
"csharp": "C#",
|
||||
"cpp": "C++",
|
||||
"clojure": "Clojure",
|
||||
"coffeescript": "CoffeeScript",
|
||||
"css": "CSS",
|
||||
"dart": "Dart",
|
||||
"diff": "Diff",
|
||||
"docker": "Docker",
|
||||
"elixir": "Elixir",
|
||||
"elm": "Elm",
|
||||
"erlang": "Erlang",
|
||||
"flow": "Flow",
|
||||
"fortran": "Fortran",
|
||||
"fsharp": "F#",
|
||||
"gherkin": "Gherkin",
|
||||
"graphql": "GraphQL",
|
||||
"groovy": "Groovy",
|
||||
"go": "Go",
|
||||
"haskell": "Haskell",
|
||||
"html": "HTML",
|
||||
"json": "JSON",
|
||||
"javascript": "JavaScript",
|
||||
"java": "Java",
|
||||
"kotlin": "Kotlin",
|
||||
"latex": "LaTeX",
|
||||
"less": "Less",
|
||||
"lisp": "Lisp",
|
||||
"livescript": "LiveScript",
|
||||
"lua": "Lua",
|
||||
"markup": "Markup",
|
||||
"markdown": "Markdown",
|
||||
"makefile": "Makefile",
|
||||
"matlab": "MATLAB",
|
||||
"nginx": "Nginx",
|
||||
"objc": "Objective-C",
|
||||
"ocaml": "OCaml",
|
||||
"pascal": "Pascal",
|
||||
"perl": "Perl",
|
||||
"php": "PHP",
|
||||
"powershell": "Power Shell",
|
||||
"prolog": "Prolog",
|
||||
"python": "Python",
|
||||
"reason": "Reason",
|
||||
"ruby": "Ruby",
|
||||
"rust": "Rust",
|
||||
"sass": "Sass",
|
||||
"scala": "Scala",
|
||||
"scheme": "Scheme",
|
||||
"scss": "SСSS",
|
||||
"shell": "Shell",
|
||||
"sql": "SQL",
|
||||
"swift": "Swift",
|
||||
"typescript": "TypeScript",
|
||||
"vbnet": "Vb.Net",
|
||||
"verilog": "Verilog",
|
||||
"vhdl": "VHDL",
|
||||
"vb": "Visual Basic",
|
||||
"wasm": "WebAssembly",
|
||||
"xml": "XML",
|
||||
"yaml": "YAML"
|
||||
}
|
|
@ -9,8 +9,8 @@
|
|||
"operators": [
|
||||
{
|
||||
"pattern": "[-+%=]=?|!=|\\*\\*?=?|\\/\\/?=?|<[<=>]?|>[=>]?|[&|^~]",
|
||||
"color": "#0366d6",
|
||||
"key": "keyword"
|
||||
"color": "#d73a49",
|
||||
"key": "operator"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
"operators": [
|
||||
{
|
||||
"pattern": "--|\\+\\+|\\*\\*=?|=>|&&=?|\\|\\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\\.{3}|\\?\\?=?|\\?\\.?|[~:]",
|
||||
"color": "#bd2c00",
|
||||
"key": "keyword"
|
||||
"color": "#d73a49",
|
||||
"key": "operator"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
|
|
|
@ -167,6 +167,7 @@ object EditorSessionModule {
|
|||
updateTitle: UpdateTitle,
|
||||
updateText: UpdateText,
|
||||
uploadBlock: UploadBlock,
|
||||
updateFields: UpdateFields,
|
||||
updateAlignment: UpdateAlignment,
|
||||
setupBookmark: SetupBookmark,
|
||||
turnIntoDocument: TurnIntoDocument,
|
||||
|
@ -209,7 +210,8 @@ object EditorSessionModule {
|
|||
move = move,
|
||||
paste = paste,
|
||||
copy = copy,
|
||||
analytics = analytics
|
||||
analytics = analytics,
|
||||
updateFields = updateFields
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -520,4 +522,11 @@ object EditorUseCaseModule {
|
|||
): TurnIntoDocument = TurnIntoDocument(
|
||||
repo = repo
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideUpdateFieldsUseCase(
|
||||
repo: BlockRepository
|
||||
): UpdateFields = UpdateFields(repo)
|
||||
}
|
|
@ -60,6 +60,7 @@ import com.anytypeio.anytype.core_utils.ext.PopupExtensions.calculateRectInWindo
|
|||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.domain.block.model.Block
|
||||
import com.anytypeio.anytype.domain.block.model.Block.Content.Text
|
||||
import com.anytypeio.anytype.domain.common.Id
|
||||
import com.anytypeio.anytype.domain.ext.getFirstLinkMarkupParam
|
||||
import com.anytypeio.anytype.domain.ext.getSubstring
|
||||
import com.anytypeio.anytype.emojifier.Emojifier
|
||||
|
@ -98,6 +99,7 @@ open class PageFragment :
|
|||
OnFragmentInteractionListener,
|
||||
AddBlockFragment.AddBlockActionReceiver,
|
||||
TurnIntoActionReceiver,
|
||||
SelectProgrammingLanguageReceiver,
|
||||
ClipboardInterceptor,
|
||||
PickiTCallbacks {
|
||||
|
||||
|
@ -730,6 +732,10 @@ open class PageFragment :
|
|||
is Command.ClearSearchInput -> {
|
||||
searchToolbar.clear()
|
||||
}
|
||||
is Command.Dialog.SelectLanguage -> {
|
||||
SelectProgrammingLanguageFragment.new(command.target)
|
||||
.show(childFragmentManager, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1179,6 +1185,11 @@ open class PageFragment :
|
|||
vm.navigateToDesktop()
|
||||
}
|
||||
|
||||
override fun onLanguageSelected(target: Id, key: String) {
|
||||
Timber.d("key: $key")
|
||||
vm.onSelectProgrammingLanguageClicked(target, key)
|
||||
}
|
||||
|
||||
//------------ End of Anytype Custom Context Menu ------------
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package com.anytypeio.anytype.ui.page.modals
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.extensions.color
|
||||
import com.anytypeio.anytype.core_ui.features.page.modal.SelectProgrammingLanguageAdapter
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
|
||||
import com.anytypeio.anytype.domain.common.Id
|
||||
import com.anytypeio.anytype.library_syntax_highlighter.obtainLanguages
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import kotlinx.android.synthetic.main.fragment_select_programming_language.*
|
||||
import timber.log.Timber
|
||||
|
||||
class SelectProgrammingLanguageFragment : BaseBottomSheetFragment() {
|
||||
|
||||
private val selectLangAdapter by lazy {
|
||||
SelectProgrammingLanguageAdapter(
|
||||
items = requireContext().obtainLanguages()
|
||||
) { lang ->
|
||||
val parent = parentFragment
|
||||
check(parent is SelectProgrammingLanguageReceiver)
|
||||
parent.onLanguageSelected(target, lang)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private val target: String
|
||||
get() = requireArguments()
|
||||
.getString(ARG_TARGET)
|
||||
?: throw IllegalStateException(MISSING_TARGET_ERROR)
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? = inflater.inflate(R.layout.fragment_select_programming_language, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.d("onViewCreated")
|
||||
dialog?.setOnShowListener { dg ->
|
||||
val bottomSheet = (dg as? BottomSheetDialog)?.findViewById<FrameLayout>(
|
||||
com.google.android.material.R.id.design_bottom_sheet
|
||||
)
|
||||
bottomSheet?.setBackgroundColor(requireContext().color(android.R.color.transparent))
|
||||
}
|
||||
recycler.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = selectLangAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {}
|
||||
override fun releaseDependencies() {}
|
||||
|
||||
companion object {
|
||||
fun new(target: Id) = SelectProgrammingLanguageFragment().apply {
|
||||
arguments = bundleOf(ARG_TARGET to target)
|
||||
}
|
||||
|
||||
private const val ARG_TARGET = "arg.select_language.target"
|
||||
private const val MISSING_TARGET_ERROR = "Target missing in args"
|
||||
}
|
||||
}
|
||||
|
||||
interface SelectProgrammingLanguageReceiver {
|
||||
fun onLanguageSelected(target: Id, key: String)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
style="@style/TopRoundedCardView"
|
||||
android:background="@color/white">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
|
@ -9,6 +9,7 @@ import android.view.View
|
|||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.Focusable
|
||||
|
@ -22,11 +23,14 @@ import com.anytypeio.anytype.core_ui.widgets.text.CodeTextInputWidget
|
|||
import com.anytypeio.anytype.core_ui.widgets.text.EditorLongClickListener
|
||||
import com.anytypeio.anytype.core_utils.ext.dimen
|
||||
import com.anytypeio.anytype.core_utils.ext.imm
|
||||
import com.anytypeio.anytype.library_syntax_highlighter.Syntaxes
|
||||
import kotlinx.android.synthetic.main.item_block_code_snippet.view.*
|
||||
import timber.log.Timber
|
||||
|
||||
class Code(view: View) : BlockViewHolder(view) {
|
||||
|
||||
val menu: TextView
|
||||
get() = itemView.code_menu
|
||||
val root: View
|
||||
get() = itemView
|
||||
val content: CodeTextInputWidget
|
||||
|
@ -92,6 +96,18 @@ class Code(view: View) : BlockViewHolder(view) {
|
|||
content.setOnClickListener {
|
||||
onTextInputClicked(item.id)
|
||||
}
|
||||
|
||||
menu.setOnClickListener {
|
||||
clicked(ListenerType.Code.SelectLanguage(item.id))
|
||||
}
|
||||
|
||||
if (!item.lang.isNullOrEmpty()) {
|
||||
content.setupSyntax(item.lang)
|
||||
menu.text = item.lang.capitalize()
|
||||
} else {
|
||||
content.setupSyntax(Syntaxes.GENERIC)
|
||||
menu.setText(R.string.block_code_menu_title)
|
||||
}
|
||||
}
|
||||
|
||||
fun indentize(item: BlockView.Indentable) {
|
||||
|
|
|
@ -472,7 +472,8 @@ sealed class BlockView : ViewType, Parcelable {
|
|||
override val isSelected: Boolean = false,
|
||||
override val color: String? = null,
|
||||
override val backgroundColor: String? = null,
|
||||
override val indent: Int = 0
|
||||
override val indent: Int = 0,
|
||||
val lang: String? = null
|
||||
) : BlockView(), Permission, Selectable, Focusable, Indentable, TextSupport {
|
||||
override fun getViewType() = HOLDER_CODE_SNIPPET
|
||||
}
|
||||
|
|
|
@ -22,21 +22,25 @@ sealed class ListenerType {
|
|||
data class Error(val target: String) : Picture()
|
||||
}
|
||||
|
||||
sealed class Video: ListenerType() {
|
||||
sealed class Video : ListenerType() {
|
||||
data class View(val target: String) : Video()
|
||||
data class Placeholder(val target: String) : Video()
|
||||
data class Upload(val target: String) : Video()
|
||||
data class Error(val target: String) : Video()
|
||||
}
|
||||
|
||||
sealed class Code : ListenerType() {
|
||||
data class SelectLanguage(val target: String) : Code()
|
||||
}
|
||||
|
||||
data class LongClick(val target: String, val dimensions: BlockDimensions) : ListenerType()
|
||||
|
||||
data class EditableBlock(val target: String) : ListenerType()
|
||||
object TitleBlock : ListenerType()
|
||||
|
||||
data class Page(val target: String): ListenerType()
|
||||
data class Page(val target: String) : ListenerType()
|
||||
|
||||
data class Mention(val target: String): ListenerType()
|
||||
data class Mention(val target: String) : ListenerType()
|
||||
|
||||
data class DividerClick(val target: String) : ListenerType()
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.anytypeio.anytype.core_ui.features.page.modal
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.features.page.modal.SelectProgrammingLanguageAdapter.Holder
|
||||
import kotlinx.android.synthetic.main.item_select_programming_language.view.*
|
||||
|
||||
class SelectProgrammingLanguageAdapter(
|
||||
private val items: List<Pair<String, String>>,
|
||||
private val onLangSelected: (String) -> Unit
|
||||
) : RecyclerView.Adapter<Holder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return Holder(
|
||||
view = inflater.inflate(
|
||||
R.layout.item_select_programming_language,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: Holder, position: Int) {
|
||||
val (key, value) = items[position]
|
||||
holder.bind(value) { onLangSelected(key) }
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
class Holder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val lang: TextView = itemView.lang
|
||||
fun bind(value: String, onClick: () -> Unit) {
|
||||
lang.text = value
|
||||
itemView.setOnClickListener { onClick() }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,4 +103,19 @@ class CodeTextInputWidget : AppCompatEditText, SyntaxHighlighter {
|
|||
}
|
||||
super.onSelectionChanged(selStart, selEnd)
|
||||
}
|
||||
|
||||
override fun setupSyntax(lang: String?) {
|
||||
if (lang == null) {
|
||||
rules.clear()
|
||||
clearHighlights()
|
||||
} else {
|
||||
val result = context.obtainSyntaxRules(lang)
|
||||
if (result.isEmpty()) {
|
||||
addRules(context.obtainGenericSyntaxRules())
|
||||
} else {
|
||||
addRules(result)
|
||||
}
|
||||
highlight()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,25 +5,25 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/item_block_code_multi_select_mode_selector"
|
||||
android:paddingTop="6dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:background="@drawable/item_block_code_multi_select_mode_selector"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp">
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/snippetContainer"
|
||||
android:background="@drawable/item_block_code_multi_select_unselected"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingStart="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:orientation="vertical">
|
||||
android:background="@drawable/item_block_code_multi_select_unselected"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/code_menu"
|
||||
|
@ -33,16 +33,23 @@
|
|||
android:layout_marginTop="@dimen/dp_12"
|
||||
android:text="@string/block_code_menu_title" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.text.CodeTextInputWidget
|
||||
android:id="@+id/snippet"
|
||||
style="@style/BlockCodeContentStyle"
|
||||
<HorizontalScrollView
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:id="@+id/scroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
android:paddingTop="23dp"
|
||||
android:paddingBottom="30dp"
|
||||
android:text="No content yet"
|
||||
tools:text="@string/default_text_placeholder" />
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.text.CodeTextInputWidget
|
||||
android:id="@+id/snippet"
|
||||
style="@style/BlockCodeContentStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
android:paddingTop="23dp"
|
||||
android:paddingBottom="30dp"
|
||||
tools:text="@string/default_text_placeholder" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lang"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:fontFamily="@font/inter_regular"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="17sp" />
|
||||
</FrameLayout>
|
|
@ -168,4 +168,9 @@ class CommandEntity {
|
|||
val targets: List<String>,
|
||||
val style: BlockEntity.Content.Divider.Style
|
||||
)
|
||||
|
||||
data class SetFields(
|
||||
val context: String,
|
||||
val fields: List<Pair<String, BlockEntity.Fields>>
|
||||
)
|
||||
}
|
|
@ -3,6 +3,8 @@ package com.anytypeio.anytype.data.auth.repo.block
|
|||
import com.anytypeio.anytype.data.auth.exception.BackwardCompatilityNotSupportedException
|
||||
import com.anytypeio.anytype.data.auth.mapper.toDomain
|
||||
import com.anytypeio.anytype.data.auth.mapper.toEntity
|
||||
import com.anytypeio.anytype.data.auth.model.BlockEntity
|
||||
import com.anytypeio.anytype.data.auth.model.CommandEntity
|
||||
import com.anytypeio.anytype.data.auth.model.PositionEntity
|
||||
import com.anytypeio.anytype.domain.base.Result
|
||||
import com.anytypeio.anytype.domain.block.model.Command
|
||||
|
@ -34,7 +36,8 @@ class BlockDataRepository(
|
|||
Result.Failure(Error.BackwardCompatibility)
|
||||
}
|
||||
|
||||
override suspend fun openProfile(id: String): Payload = factory.remote.openProfile(id).toDomain()
|
||||
override suspend fun openProfile(id: String): Payload =
|
||||
factory.remote.openProfile(id).toDomain()
|
||||
|
||||
override suspend fun closeDashboard(id: String) {
|
||||
factory.remote.closeDashboard(id)
|
||||
|
@ -42,7 +45,7 @@ class BlockDataRepository(
|
|||
|
||||
override suspend fun updateAlignment(
|
||||
command: Command.UpdateAlignment
|
||||
) : Payload = factory.remote.updateAlignment(command.toEntity()).toDomain()
|
||||
): Payload = factory.remote.updateAlignment(command.toEntity()).toDomain()
|
||||
|
||||
override suspend fun createPage(parentId: String, emoji: String?) =
|
||||
factory.remote.createPage(parentId, emoji)
|
||||
|
@ -61,7 +64,7 @@ class BlockDataRepository(
|
|||
|
||||
override suspend fun updateTextStyle(
|
||||
command: Command.UpdateStyle
|
||||
) : Payload = factory.remote.updateTextStyle(command.toEntity()).toDomain()
|
||||
): Payload = factory.remote.updateTextStyle(command.toEntity()).toDomain()
|
||||
|
||||
override suspend fun updateTextColor(
|
||||
command: Command.UpdateTextColor
|
||||
|
@ -144,11 +147,11 @@ class BlockDataRepository(
|
|||
|
||||
override suspend fun undo(
|
||||
command: Command.Undo
|
||||
) : Payload = factory.remote.undo(command.toEntity()).toDomain()
|
||||
): Payload = factory.remote.undo(command.toEntity()).toDomain()
|
||||
|
||||
override suspend fun redo(
|
||||
command: Command.Redo
|
||||
) : Payload = factory.remote.redo(command.toEntity()).toDomain()
|
||||
): Payload = factory.remote.redo(command.toEntity()).toDomain()
|
||||
|
||||
override suspend fun archiveDocument(
|
||||
command: Command.ArchiveDocument
|
||||
|
@ -190,6 +193,18 @@ class BlockDataRepository(
|
|||
position = PositionEntity.valueOf(position.name)
|
||||
).toDomain()
|
||||
|
||||
override suspend fun updateDivider(command: Command.UpdateDivider): Payload =
|
||||
factory.remote.updateDivider(command = command.toEntity()).toDomain()
|
||||
override suspend fun updateDivider(
|
||||
command: Command.UpdateDivider
|
||||
): Payload = factory.remote.updateDivider(command = command.toEntity()).toDomain()
|
||||
|
||||
override suspend fun setFields(
|
||||
command: Command.SetFields
|
||||
): Payload = factory.remote.setFields(
|
||||
command = CommandEntity.SetFields(
|
||||
context = command.context,
|
||||
fields = command.fields.map { (id, fields) ->
|
||||
id to BlockEntity.Fields(fields.map.toMutableMap())
|
||||
}
|
||||
)
|
||||
).toDomain()
|
||||
}
|
|
@ -56,5 +56,7 @@ interface BlockDataStore {
|
|||
position: PositionEntity
|
||||
): PayloadEntity
|
||||
|
||||
suspend fun updateDivider(command: CommandEntity.UpdateDivider) : PayloadEntity
|
||||
suspend fun updateDivider(command: CommandEntity.UpdateDivider): PayloadEntity
|
||||
|
||||
suspend fun setFields(command: CommandEntity.SetFields): PayloadEntity
|
||||
}
|
|
@ -56,5 +56,7 @@ interface BlockRemote {
|
|||
position: PositionEntity
|
||||
): PayloadEntity
|
||||
|
||||
suspend fun updateDivider(command: CommandEntity.UpdateDivider) : PayloadEntity
|
||||
suspend fun updateDivider(command: CommandEntity.UpdateDivider): PayloadEntity
|
||||
|
||||
suspend fun setFields(command: CommandEntity.SetFields): PayloadEntity
|
||||
}
|
|
@ -148,6 +148,11 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
|
|||
position = position
|
||||
)
|
||||
|
||||
override suspend fun updateDivider(command: CommandEntity.UpdateDivider): PayloadEntity =
|
||||
remote.updateDivider(command)
|
||||
override suspend fun updateDivider(
|
||||
command: CommandEntity.UpdateDivider
|
||||
): PayloadEntity = remote.updateDivider(command)
|
||||
|
||||
override suspend fun setFields(
|
||||
command: CommandEntity.SetFields
|
||||
): PayloadEntity = remote.setFields(command)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.anytypeio.anytype.domain.block.interactor
|
||||
|
||||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
import com.anytypeio.anytype.domain.block.model.Block
|
||||
import com.anytypeio.anytype.domain.block.model.Command
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.common.Id
|
||||
import com.anytypeio.anytype.domain.event.model.Payload
|
||||
|
||||
class UpdateFields(private val repo: BlockRepository) :
|
||||
BaseUseCase<Payload, UpdateFields.Params>() {
|
||||
|
||||
override suspend fun run(params: Params) = safe {
|
||||
repo.setFields(
|
||||
command = Command.SetFields(
|
||||
context = params.context,
|
||||
fields = params.fields
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val context: Id,
|
||||
val fields: List<Pair<Id, Block.Fields>>
|
||||
)
|
||||
}
|
|
@ -32,6 +32,7 @@ data class Block(
|
|||
val iconEmoji: String? by default
|
||||
val iconImage: String? by default
|
||||
val isArchived: Boolean? by default
|
||||
val lang: String? by default
|
||||
|
||||
companion object {
|
||||
fun empty(): Fields = Fields(emptyMap())
|
||||
|
|
|
@ -317,4 +317,9 @@ sealed class Command {
|
|||
val targets: List<Id>,
|
||||
val style: Block.Content.Divider.Style
|
||||
)
|
||||
|
||||
data class SetFields(
|
||||
val context: Id,
|
||||
val fields: List<Pair<Id, Block.Fields>>
|
||||
)
|
||||
}
|
|
@ -112,4 +112,6 @@ interface BlockRepository {
|
|||
): Payload
|
||||
|
||||
suspend fun updateDivider(command: Command.UpdateDivider): Payload
|
||||
|
||||
suspend fun setFields(command: Command.SetFields): Payload
|
||||
}
|
|
@ -12,6 +12,8 @@ interface SyntaxHighlighter {
|
|||
val source: Editable
|
||||
val rules: MutableList<Syntax>
|
||||
|
||||
fun setupSyntax(lang: String?)
|
||||
|
||||
fun addRules(new: List<Syntax>) {
|
||||
rules.apply {
|
||||
clear()
|
||||
|
@ -20,7 +22,7 @@ interface SyntaxHighlighter {
|
|||
}
|
||||
|
||||
fun highlight() {
|
||||
clear()
|
||||
clearHighlights()
|
||||
rules.forEach { syntax ->
|
||||
val matcher = syntax.matcher(source.toString())
|
||||
while (matcher.find()) {
|
||||
|
@ -34,7 +36,7 @@ interface SyntaxHighlighter {
|
|||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
fun clearHighlights() {
|
||||
val current = source.getSpans(0, source.length, SyntaxColorSpan::class.java)
|
||||
current.forEach { span -> source.removeSpan(span) }
|
||||
}
|
||||
|
|
|
@ -3,4 +3,5 @@ package com.anytypeio.anytype.library_syntax_highlighter
|
|||
object Syntaxes {
|
||||
const val KOTLIN = "kotlin"
|
||||
const val PYTHON = "python"
|
||||
const val GENERIC = "generic"
|
||||
}
|
|
@ -4,19 +4,36 @@ import android.content.Context
|
|||
import android.graphics.Color
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import java.io.IOException
|
||||
|
||||
fun Context.obtainSyntaxRules(language: String): List<Syntax> {
|
||||
val path = "syntax/${language}.json"
|
||||
val json = obtainJsonDataFromAsset(path)
|
||||
checkNotNull(json) { "Could not deserialize syntax rules from path: $path" }
|
||||
val descriptor = Json.decodeFromString<SyntaxDescriptor>(json)
|
||||
val rules = descriptor.let { it.keywords + it.operators + it.other }
|
||||
return rules.map { s ->
|
||||
Syntax(
|
||||
regex = s.pattern,
|
||||
color = Color.parseColor(s.color)
|
||||
)
|
||||
return if (json != null) {
|
||||
val descriptor = Json.decodeFromString<SyntaxDescriptor>(json)
|
||||
val rules = descriptor.let { it.keywords + it.operators + it.other }
|
||||
rules.map { s ->
|
||||
Syntax(
|
||||
regex = s.pattern,
|
||||
color = Color.parseColor(s.color)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.obtainGenericSyntaxRules(): List<Syntax> {
|
||||
return obtainSyntaxRules(Syntaxes.GENERIC)
|
||||
}
|
||||
|
||||
fun Context.obtainLanguages(): List<Pair<String, String>> {
|
||||
val json = obtainJsonDataFromAsset("syntax/languages.json")
|
||||
checkNotNull(json) { "Json data for languages is missing" }
|
||||
return Json.parseToJsonElement(json).jsonObject.map { (key, element) ->
|
||||
key to element.jsonPrimitive.content
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -165,6 +165,11 @@ class BlockMiddleware(
|
|||
position: PositionEntity
|
||||
): PayloadEntity = middleware.linkToObject(context, target, block, replace, position)
|
||||
|
||||
override suspend fun updateDivider(command: CommandEntity.UpdateDivider): PayloadEntity =
|
||||
middleware.updateDividerStyle(command)
|
||||
override suspend fun updateDivider(
|
||||
command: CommandEntity.UpdateDivider
|
||||
): PayloadEntity = middleware.updateDividerStyle(command)
|
||||
|
||||
override suspend fun setFields(
|
||||
command: CommandEntity.SetFields
|
||||
): PayloadEntity = middleware.setFields(command)
|
||||
}
|
|
@ -14,6 +14,7 @@ import com.anytypeio.anytype.middleware.service.MiddlewareService;
|
|||
import com.google.protobuf.Struct;
|
||||
import com.google.protobuf.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import anytype.Commands;
|
||||
|
@ -1193,4 +1194,36 @@ public class Middleware {
|
|||
|
||||
return mapper.toPayload(response.getEvent());
|
||||
}
|
||||
|
||||
public PayloadEntity setFields(CommandEntity.SetFields command) throws Exception {
|
||||
|
||||
List<BlockList.Set.Fields.Request.BlockField> fields = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < command.getFields().size(); i++) {
|
||||
Pair<String, BlockEntity.Fields> item = command.getFields().get(i);
|
||||
BlockList.Set.Fields.Request.BlockField field = BlockList.Set.Fields.Request.BlockField
|
||||
.newBuilder()
|
||||
.setBlockId(item.getFirst())
|
||||
.setFields(mapper.toMiddleware(item.getSecond()))
|
||||
.build();
|
||||
fields.add(field);
|
||||
}
|
||||
|
||||
BlockList.Set.Fields.Request request = BlockList.Set.Fields.Request.newBuilder()
|
||||
.setContextId(command.getContext())
|
||||
.addAllBlockFields(fields)
|
||||
.build();
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.d(request.getClass().getName() + "\n" + request.toString());
|
||||
}
|
||||
|
||||
BlockList.Set.Fields.Response response = service.blockListSetFields(request);
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.d(response.getClass().getName() + "\n" + response.toString());
|
||||
}
|
||||
|
||||
return mapper.toPayload(response.getEvent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ import com.anytypeio.anytype.data.auth.model.PayloadEntity
|
|||
import com.anytypeio.anytype.data.auth.model.PositionEntity
|
||||
import com.anytypeio.anytype.middleware.converters.block
|
||||
import com.anytypeio.anytype.middleware.converters.blocks
|
||||
import com.anytypeio.anytype.middleware.converters.fields
|
||||
import com.anytypeio.anytype.middleware.converters.toMiddleware
|
||||
import com.google.protobuf.Struct
|
||||
|
||||
class MiddlewareMapper {
|
||||
|
||||
|
@ -46,7 +48,11 @@ class MiddlewareMapper {
|
|||
return alignment.toMiddleware()
|
||||
}
|
||||
|
||||
fun toEntity(blocks: List<Block>) : List<BlockEntity> {
|
||||
fun toEntity(blocks: List<Block>): List<BlockEntity> {
|
||||
return blocks.blocks()
|
||||
}
|
||||
|
||||
fun toMiddleware(fields: BlockEntity.Fields): Struct {
|
||||
return fields.fields()
|
||||
}
|
||||
}
|
|
@ -424,4 +424,15 @@ public class DefaultMiddlewareService implements MiddlewareService {
|
|||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockList.Set.Fields.Response blockListSetFields(BlockList.Set.Fields.Request request) throws Exception {
|
||||
byte[] encoded = Service.blockListSetFields(request.toByteArray());
|
||||
BlockList.Set.Fields.Response response = BlockList.Set.Fields.Response.parseFrom(encoded);
|
||||
if (response.getError() != null && response.getError().getCode() != BlockList.Set.Fields.Response.Error.Code.NULL) {
|
||||
throw new Exception(response.getError().getDescription());
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,4 +86,6 @@ public interface MiddlewareService {
|
|||
Commands.Rpc.Page.Create.Response pageCreate(Commands.Rpc.Page.Create.Request request) throws Exception;
|
||||
|
||||
Commands.Rpc.Version.Get.Response getVersion(Commands.Rpc.Version.Get.Request request) throws Exception;
|
||||
|
||||
BlockList.Set.Fields.Response blockListSetFields(BlockList.Set.Fields.Request request) throws Exception;
|
||||
}
|
||||
|
|
|
@ -587,4 +587,70 @@ class MiddlewareTest {
|
|||
verify(service, times(1)).uploadFile(request)
|
||||
verifyNoMoreInteractions(service)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should create request for setting block fields`() {
|
||||
|
||||
// SETUP
|
||||
|
||||
val ctx = MockDataFactory.randomUuid()
|
||||
|
||||
val block1 = MockDataFactory.randomUuid()
|
||||
val block2 = MockDataFactory.randomUuid()
|
||||
|
||||
val command = CommandEntity.SetFields(
|
||||
context = ctx,
|
||||
fields = listOf(
|
||||
Pair(
|
||||
block1,
|
||||
BlockEntity.Fields(
|
||||
map = mutableMapOf(
|
||||
"lang" to "kotlin"
|
||||
)
|
||||
)
|
||||
),
|
||||
Pair(
|
||||
block2,
|
||||
BlockEntity.Fields(
|
||||
map = mutableMapOf(
|
||||
"lang" to "python"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val fields = listOf(
|
||||
BlockList.Set.Fields.Request.BlockField.newBuilder()
|
||||
.setBlockId(block1)
|
||||
.setFields(
|
||||
Struct.newBuilder()
|
||||
.putFields("lang", Value.newBuilder().setStringValue("kotlin").build())
|
||||
)
|
||||
.build(),
|
||||
BlockList.Set.Fields.Request.BlockField.newBuilder()
|
||||
.setBlockId(block2)
|
||||
.setFields(
|
||||
Struct.newBuilder()
|
||||
.putFields("lang", Value.newBuilder().setStringValue("python").build())
|
||||
)
|
||||
.build()
|
||||
)
|
||||
|
||||
val request = BlockList.Set.Fields.Request.newBuilder()
|
||||
.setContextId(ctx)
|
||||
.addAllBlockFields(fields)
|
||||
.build()
|
||||
|
||||
service.stub {
|
||||
on { blockListSetFields(request) } doReturn BlockList.Set.Fields.Response.getDefaultInstance()
|
||||
}
|
||||
|
||||
// TESTING
|
||||
|
||||
middleware.setFields(command)
|
||||
|
||||
verify(service, times(1)).blockListSetFields(request)
|
||||
verifyNoMoreInteractions(service)
|
||||
}
|
||||
}
|
|
@ -2632,6 +2632,12 @@ class PageViewModel(
|
|||
else -> Unit
|
||||
}
|
||||
}
|
||||
is ListenerType.Code.SelectLanguage -> {
|
||||
when (mode) {
|
||||
EditorMode.EDITING -> dispatch(Command.Dialog.SelectLanguage(clicked.target))
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onPlusButtonPressed() {
|
||||
|
@ -2924,6 +2930,24 @@ class PageViewModel(
|
|||
} ?: run { false }
|
||||
}
|
||||
|
||||
fun onSelectProgrammingLanguageClicked(target: Id, key: String) {
|
||||
viewModelScope.launch {
|
||||
orchestrator.proxies.intents.send(
|
||||
Intent.CRUD.UpdateFields(
|
||||
context = context,
|
||||
fields = listOf(
|
||||
Pair(
|
||||
target,
|
||||
Block.Fields(
|
||||
mapOf("lang" to key)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NO_SEARCH_RESULT_POSITION = -1
|
||||
const val EMPTY_TEXT = ""
|
||||
|
|
|
@ -76,4 +76,8 @@ sealed class Command {
|
|||
val target: Id,
|
||||
val url: Url
|
||||
) : Command()
|
||||
|
||||
sealed class Dialog : Command() {
|
||||
data class SelectLanguage(val target: String) : Dialog()
|
||||
}
|
||||
}
|
|
@ -63,6 +63,11 @@ sealed class Intent {
|
|||
val next: Id?,
|
||||
val effects: List<SideEffect> = emptyList()
|
||||
) : CRUD()
|
||||
|
||||
class UpdateFields(
|
||||
val context: Id,
|
||||
val fields: List<Pair<Id, Block.Fields>>
|
||||
) : CRUD()
|
||||
}
|
||||
|
||||
sealed class Clipboard : Intent() {
|
||||
|
|
|
@ -64,6 +64,7 @@ class Orchestrator(
|
|||
private val uploadBlock: UploadBlock,
|
||||
private val setupBookmark: SetupBookmark,
|
||||
private val turnIntoDocument: TurnIntoDocument,
|
||||
private val updateFields: UpdateFields,
|
||||
private val move: Move,
|
||||
private val copy: Copy,
|
||||
private val paste: Paste,
|
||||
|
@ -201,6 +202,17 @@ class Orchestrator(
|
|||
}
|
||||
)
|
||||
}
|
||||
is Intent.CRUD.UpdateFields -> {
|
||||
updateFields(
|
||||
params = UpdateFields.Params(
|
||||
context = intent.context,
|
||||
fields = intent.fields
|
||||
)
|
||||
).proceed(
|
||||
failure = {},
|
||||
success = { proxies.payloads.send(it) }
|
||||
)
|
||||
}
|
||||
is Intent.Text.Split -> {
|
||||
val startTime = System.currentTimeMillis()
|
||||
splitBlock(
|
||||
|
|
|
@ -477,7 +477,8 @@ class DefaultBlockViewRenderer(
|
|||
backgroundColor = content.backgroundColor,
|
||||
color = content.color,
|
||||
isFocused = block.id == focus.id,
|
||||
indent = indent
|
||||
indent = indent,
|
||||
lang = block.fields.lang
|
||||
)
|
||||
|
||||
private fun highlight(
|
||||
|
|
|
@ -174,7 +174,10 @@ open class PageViewModelTest {
|
|||
lateinit var turnIntoDocument: TurnIntoDocument
|
||||
|
||||
@Mock
|
||||
lateinit var gateway : Gateway
|
||||
lateinit var updateFields: UpdateFields
|
||||
|
||||
@Mock
|
||||
lateinit var gateway: Gateway
|
||||
|
||||
@Mock
|
||||
lateinit var analytics: Analytics
|
||||
|
@ -3857,7 +3860,8 @@ open class PageViewModelTest {
|
|||
copy = copy,
|
||||
move = move,
|
||||
turnIntoDocument = turnIntoDocument,
|
||||
analytics = analytics
|
||||
analytics = analytics,
|
||||
updateFields = updateFields
|
||||
),
|
||||
bridge = Bridge()
|
||||
)
|
||||
|
|
|
@ -105,6 +105,9 @@ open class EditorPresentationTestSetup {
|
|||
@Mock
|
||||
lateinit var uploadBlock: UploadBlock
|
||||
|
||||
@Mock
|
||||
lateinit var updateFields: UpdateFields
|
||||
|
||||
@Mock
|
||||
lateinit var paste: Paste
|
||||
|
||||
|
@ -205,7 +208,8 @@ open class EditorPresentationTestSetup {
|
|||
copy = copy,
|
||||
move = move,
|
||||
turnIntoDocument = turnIntoDocument,
|
||||
analytics = analytics
|
||||
analytics = analytics,
|
||||
updateFields = updateFields
|
||||
),
|
||||
bridge = Bridge()
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue