mirror of
https://github.com/k0shk0sh/FastHub.git
synced 2025-12-08 19:05:54 +00:00
use recyclerview for markwon
This commit is contained in:
parent
efe7a89714
commit
aae3d0fd09
@ -13,21 +13,25 @@ import com.fastaccess.github.ui.modules.issue.fragment.IssueFragment
|
||||
import com.fastaccess.github.usecase.search.FilterSearchUsersUseCase
|
||||
import com.fastaccess.github.utils.extensions.theme
|
||||
import com.fastaccess.markdown.GrammarLocatorDef
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.EmojiPlugin
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.ext.latex.JLatexMathPlugin
|
||||
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
|
||||
import io.noties.markwon.ext.tables.TablePlugin
|
||||
import io.noties.markwon.ext.tasklist.TaskListPlugin
|
||||
import io.noties.markwon.html.HtmlPlugin
|
||||
import io.noties.markwon.image.glide.GlideImagesPlugin
|
||||
import io.noties.markwon.linkify.LinkifyPlugin
|
||||
import io.noties.markwon.recycler.MarkwonAdapter
|
||||
import io.noties.markwon.recycler.SimpleEntry
|
||||
import io.noties.markwon.recycler.table.TableEntry
|
||||
import io.noties.markwon.recycler.table.TableEntryPlugin
|
||||
import io.noties.markwon.syntax.Prism4jThemeDarkula
|
||||
import io.noties.markwon.syntax.Prism4jThemeDefault
|
||||
import io.noties.markwon.syntax.SyntaxHighlightPlugin
|
||||
import io.noties.prism4j.Prism4j
|
||||
import org.commonmark.ext.gfm.tables.TableBlock
|
||||
import org.commonmark.node.FencedCodeBlock
|
||||
|
||||
/**
|
||||
* Created by Kosh on 02.02.19.
|
||||
@ -46,7 +50,7 @@ class FragmentModule {
|
||||
.usePlugin(TaskListPlugin.create(context))
|
||||
.usePlugin(HtmlPlugin.create())
|
||||
.usePlugin(GlideImagesPlugin.create(context))
|
||||
.usePlugin(TablePlugin.create(context))
|
||||
.usePlugin(TableEntryPlugin.create(context))
|
||||
.usePlugin(StrikethroughPlugin.create())
|
||||
.usePlugin(LinkifyPlugin.create(Linkify.EMAIL_ADDRESSES or Linkify.WEB_URLS))
|
||||
.usePlugin(
|
||||
@ -58,9 +62,16 @@ class FragmentModule {
|
||||
}
|
||||
)
|
||||
)
|
||||
.usePlugin(EmojiPlugin.create())
|
||||
.build()
|
||||
|
||||
@PerFragment @Provides fun provideMarkwonAdapterBuilder(): MarkwonAdapter.Builder =
|
||||
MarkwonAdapter.builder(R.layout.markdown_textview_row_item, R.id.text)
|
||||
.include(FencedCodeBlock::class.java, SimpleEntry.create(R.layout.markwon_fenced_cod_block_row_item, R.id.text))
|
||||
.include(TableBlock::class.java, TableEntry.create {
|
||||
it.tableLayout(R.layout.markwon_table_row_item, R.id.table_layout)
|
||||
.textLayoutIsRoot(R.layout.markwon_table_entry_cell)
|
||||
})
|
||||
|
||||
@PerFragment @Provides fun provideMentionsPresenter(
|
||||
context: Context,
|
||||
searchUsersUseCase: FilterSearchUsersUseCase
|
||||
|
||||
@ -11,13 +11,15 @@ import com.fastaccess.github.ui.adapter.viewholder.CommentViewHolder
|
||||
import com.fastaccess.github.ui.adapter.viewholder.IssueContentViewHolder
|
||||
import com.fastaccess.github.ui.adapter.viewholder.LoadingViewHolder
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.recycler.MarkwonAdapter
|
||||
|
||||
/**
|
||||
* Created by Kosh on 20.01.19.
|
||||
*/
|
||||
class IssueTimelineAdapter(
|
||||
private val markwon: Markwon,
|
||||
private val theme: Int
|
||||
private val theme: Int,
|
||||
private val markwonAdapterBuilder: MarkwonAdapter.Builder
|
||||
) : ListAdapter<TimelineModel, RecyclerView.ViewHolder>(DIFF_CALLBACK) {
|
||||
|
||||
private val notifyCallback by lazy {
|
||||
@ -36,15 +38,21 @@ class IssueTimelineAdapter(
|
||||
} ?: super.getItemViewType(position)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
COMMENT -> CommentViewHolder(parent, markwon, theme, notifyCallback)
|
||||
COMMENT -> CommentViewHolder(parent, markwon, theme, notifyCallback, markwonAdapterBuilder)
|
||||
CONTENT -> IssueContentViewHolder(parent)
|
||||
else -> LoadingViewHolder<Any>(parent).apply { itemView.isVisible = false }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int
|
||||
) {
|
||||
when (holder) {
|
||||
is CommentViewHolder -> holder.bind(getItem(position).comment)
|
||||
is IssueContentViewHolder -> holder.bind(getItem(position))
|
||||
@ -68,8 +76,15 @@ class IssueTimelineAdapter(
|
||||
private const val CONTENT = 3
|
||||
|
||||
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<TimelineModel?>() {
|
||||
override fun areItemsTheSame(oldItem: TimelineModel, newItem: TimelineModel): Boolean = oldItem.hashCode() == newItem.hashCode()
|
||||
override fun areContentsTheSame(oldItem: TimelineModel, newItem: TimelineModel): Boolean = oldItem == newItem
|
||||
override fun areItemsTheSame(
|
||||
oldItem: TimelineModel,
|
||||
newItem: TimelineModel
|
||||
): Boolean = oldItem.hashCode() == newItem.hashCode()
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: TimelineModel,
|
||||
newItem: TimelineModel
|
||||
): Boolean = oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,19 @@
|
||||
package com.fastaccess.github.ui.adapter.viewholder
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.util.Linkify
|
||||
import android.util.Patterns
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.fastaccess.data.model.CommentAuthorAssociation
|
||||
import com.fastaccess.data.model.CommentModel
|
||||
import com.fastaccess.data.model.getEmoji
|
||||
import com.fastaccess.github.base.engine.ThemeEngine
|
||||
import com.fastaccess.github.R
|
||||
import com.fastaccess.github.extensions.timeAgo
|
||||
import com.fastaccess.github.ui.adapter.base.BaseViewHolder
|
||||
import com.fastaccess.github.utils.extensions.popupEmoji
|
||||
import com.fastaccess.markdown.MarkdownProvider
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.recycler.MarkwonAdapter
|
||||
import kotlinx.android.synthetic.main.comment_row_item.view.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
/**
|
||||
@ -27,10 +24,11 @@ class CommentViewHolder(
|
||||
parent: ViewGroup,
|
||||
private val markwon: Markwon,
|
||||
private val theme: Int,
|
||||
private val callback: (position: Int) -> Unit
|
||||
private val callback: (position: Int) -> Unit,
|
||||
private val markwonAdapterBuilder: MarkwonAdapter.Builder
|
||||
) : BaseViewHolder<CommentModel?>(
|
||||
LayoutInflater.from(parent.context)
|
||||
.inflate(com.fastaccess.github.R.layout.comment_row_item, parent, false)
|
||||
.inflate(R.layout.comment_row_item, parent, false)
|
||||
) {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@ -47,24 +45,23 @@ class CommentViewHolder(
|
||||
} else {
|
||||
"${model.authorAssociation?.value?.toLowerCase()?.replace("_", "")} ${model.updatedAt?.timeAgo()}"
|
||||
}
|
||||
val adapter = markwonAdapterBuilder.build()
|
||||
descriptionRecyclerView.adapter = adapter
|
||||
adapter.setMarkdown(markwon, model.body ?: resources.getString(R.string.no_description_provided))
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
MarkdownProvider.loadIntoTextView(
|
||||
markwon, description, model.body ?: "", ThemeEngine.getCodeBackground(theme),
|
||||
ThemeEngine.isLightTheme(theme)
|
||||
)
|
||||
|
||||
val filter = Linkify.TransformFilter { match, _ -> match.group() }
|
||||
|
||||
val mentionPattern = Pattern.compile("@([A-Za-z0-9_-]+)")
|
||||
val mentionScheme = "https://www.github.com/"
|
||||
Linkify.addLinks(description, mentionPattern, mentionScheme, null, filter)
|
||||
|
||||
val hashtagPattern = Pattern.compile("#([A-Za-z0-9_-]+)")
|
||||
val hashtagScheme = "https://www.github.com/"
|
||||
Linkify.addLinks(description, hashtagPattern, hashtagScheme, null, filter)
|
||||
|
||||
val urlPattern = Patterns.WEB_URL
|
||||
Linkify.addLinks(description, urlPattern, null, null, filter)
|
||||
// val filter = Linkify.TransformFilter { match, _ -> match.group() }
|
||||
// val mentionPattern = Pattern.compile("@([A-Za-z0-9_-]+)")
|
||||
// val mentionScheme = "https://www.github.com/"
|
||||
// Linkify.addLinks(description, mentionPattern, mentionScheme, null, filter)
|
||||
//
|
||||
// val hashtagPattern = Pattern.compile("#([A-Za-z0-9_-]+)")
|
||||
// val hashtagScheme = "https://www.github.com/"
|
||||
// Linkify.addLinks(description, hashtagPattern, hashtagScheme, null, filter)
|
||||
//
|
||||
// val urlPattern = Patterns.WEB_URL
|
||||
// Linkify.addLinks(description, urlPattern, null, null, filter)
|
||||
|
||||
addEmoji.setOnClickListener {
|
||||
it.popupEmoji(requireNotNull(model.id), model.reactionGroups) {
|
||||
|
||||
@ -21,7 +21,6 @@ import com.fastaccess.data.storage.FastHubSharedPreference
|
||||
import com.fastaccess.github.R
|
||||
import com.fastaccess.github.base.BaseFragment
|
||||
import com.fastaccess.github.base.BaseViewModel
|
||||
import com.fastaccess.github.base.engine.ThemeEngine
|
||||
import com.fastaccess.github.extensions.*
|
||||
import com.fastaccess.github.platform.mentions.MentionsPresenter
|
||||
import com.fastaccess.github.ui.adapter.IssueTimelineAdapter
|
||||
@ -38,7 +37,6 @@ import com.fastaccess.github.utils.WEB_EDITOR_DEEPLINK
|
||||
import com.fastaccess.github.utils.extensions.isConnected
|
||||
import com.fastaccess.github.utils.extensions.popupEmoji
|
||||
import com.fastaccess.github.utils.extensions.theme
|
||||
import com.fastaccess.markdown.MarkdownProvider
|
||||
import com.fastaccess.markdown.spans.LabelSpan
|
||||
import com.fastaccess.markdown.widget.SpannableBuilder
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
@ -49,6 +47,7 @@ import github.type.CommentAuthorAssociation
|
||||
import github.type.IssueState
|
||||
import github.type.LockReason
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.recycler.MarkwonAdapter
|
||||
import kotlinx.android.synthetic.main.empty_state_layout.*
|
||||
import kotlinx.android.synthetic.main.issue_header_row_item.*
|
||||
import kotlinx.android.synthetic.main.issue_pr_fragment_layout.*
|
||||
@ -66,12 +65,14 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
|
||||
@Inject lateinit var markwon: Markwon
|
||||
@Inject lateinit var preference: FastHubSharedPreference
|
||||
@Inject lateinit var mentionsPresenter: MentionsPresenter
|
||||
@Inject lateinit var markwonAdapterBuilder: MarkwonAdapter.Builder
|
||||
|
||||
private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(IssueTimelineViewModel::class.java) }
|
||||
private val login by lazy { arguments?.getString(EXTRA) ?: "" }
|
||||
private val repo by lazy { arguments?.getString(EXTRA_TWO) ?: "" }
|
||||
private val number by lazy { arguments?.getInt(EXTRA_THREE) ?: 0 }
|
||||
private val adapter by lazy { IssueTimelineAdapter(markwon, preference.theme) }
|
||||
private val markwonAdapter by lazy { markwonAdapterBuilder.build() }
|
||||
private val adapter by lazy { IssueTimelineAdapter(markwon, preference.theme, markwonAdapterBuilder) }
|
||||
|
||||
override fun layoutRes(): Int = R.layout.issue_pr_fragment_layout
|
||||
override fun viewModel(): BaseViewModel? = viewModel
|
||||
@ -239,10 +240,11 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
|
||||
} else {
|
||||
"${model.authorAssociation?.toLowerCase()?.replace("_", "")} ${model.updatedAt?.timeAgo()}"
|
||||
}
|
||||
MarkdownProvider.loadIntoTextView(
|
||||
markwon, description, model.body ?: "", ThemeEngine.getCodeBackground(theme),
|
||||
ThemeEngine.isLightTheme(theme)
|
||||
)
|
||||
|
||||
descriptionRecyclerView.adapter = markwonAdapter
|
||||
markwonAdapter.setMarkdown(markwon, model.body ?: "**${getString(R.string.no_description_provided)}**")
|
||||
markwonAdapter.notifyDataSetChanged()
|
||||
|
||||
state.text = model.state?.toLowerCase()
|
||||
state.setChipBackgroundColorResource(
|
||||
if (IssueState.OPEN.rawValue().equals(model.state, true)) {
|
||||
|
||||
@ -7,19 +7,28 @@ import com.fastaccess.github.R
|
||||
import com.fastaccess.github.extensions.getDrawableCompat
|
||||
import com.fastaccess.github.extensions.route
|
||||
import com.fastaccess.github.platform.glide.GlideApp
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
/**
|
||||
* Created by Kosh on 27.12.18.
|
||||
*/
|
||||
class AvatarImageView constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0) : AppCompatImageView(context, attrs, defStyle) {
|
||||
class AvatarImageView constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : AppCompatImageView(context, attrs, defStyle) {
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?
|
||||
) : this(context, attrs, 0)
|
||||
|
||||
|
||||
fun loadAvatar(url: String? = null, userUrl: String? = null) {
|
||||
fun loadAvatar(
|
||||
url: String? = null,
|
||||
userUrl: String? = null
|
||||
) {
|
||||
setBackgroundResource(R.drawable.circle_shape)
|
||||
if (url.isNullOrEmpty()) {
|
||||
setImageResource(R.drawable.ic_profile)
|
||||
@ -34,6 +43,9 @@ class AvatarImageView constructor(context: Context,
|
||||
.dontAnimate()
|
||||
.into(this)
|
||||
}
|
||||
userUrl?.let { setOnClickListener { it.context.route(userUrl) } }
|
||||
userUrl?.let { userUrl ->
|
||||
Timber.e(userUrl)
|
||||
setOnClickListener { it.context.route(userUrl) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -38,7 +38,7 @@ class BaseRecyclerView constructor(context: Context,
|
||||
}
|
||||
}
|
||||
|
||||
override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
|
||||
override fun setAdapter(adapter: Adapter<*>?) {
|
||||
super.setAdapter(adapter)
|
||||
if (isInEditMode) return
|
||||
if (adapter != null) {
|
||||
|
||||
@ -95,14 +95,14 @@
|
||||
android:src="@drawable/ic_overflow" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/description"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/descriptionRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="Place the truffels in an ice blender, and toss immediately with tangy lime.immediately with tangy lime" />
|
||||
android:layout_marginTop="@dimen/spacing_micro"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="@string/llm" />
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@ -151,15 +151,14 @@
|
||||
android:src="@drawable/ic_overflow" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/description"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/descriptionRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/spacing_micro"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="Place the truffels in an ice blender, and toss immediately with tangy lime.immediately with tangy lime" />
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="@string/llm" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.AppCompatTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:textIsSelectable="true" />
|
||||
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:scrollbarStyle="outsideInset">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/text"
|
||||
style="@style/LayoutPadding"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:text="Table content" />
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:scrollbarStyle="outsideInset">
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/table_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="*" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
@ -1,22 +0,0 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import org.commonmark.node.CustomNode
|
||||
import org.commonmark.node.Delimited
|
||||
|
||||
/**
|
||||
* Created by kosh on 20/08/2017.
|
||||
*/
|
||||
|
||||
class Emoji : CustomNode(), Delimited {
|
||||
var emoji: String? = null
|
||||
|
||||
override fun getOpeningDelimiter(): String = DELIMITER
|
||||
|
||||
override fun getClosingDelimiter(): String = DELIMITER
|
||||
|
||||
override fun toString(): String = emoji ?: "no emoji"
|
||||
|
||||
companion object {
|
||||
private const val DELIMITER = ":"
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.internal.EmojiDelimiterProcessor
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.internal.EmojiNodeRenderer
|
||||
import org.commonmark.Extension
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
|
||||
/**
|
||||
* Created by kosh on 20/08/2017.
|
||||
*/
|
||||
|
||||
class EmojiExtension private constructor() : Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {
|
||||
|
||||
override fun extend(parserBuilder: Parser.Builder) {
|
||||
parserBuilder.customDelimiterProcessor(EmojiDelimiterProcessor())
|
||||
}
|
||||
|
||||
override fun extend(rendererBuilder: HtmlRenderer.Builder) {
|
||||
rendererBuilder.nodeRendererFactory { EmojiNodeRenderer(it) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(): Extension {
|
||||
return EmojiExtension()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import android.text.SpannedString
|
||||
import com.fastaccess.markdown.emoji.EmojiManager
|
||||
import io.noties.markwon.AbstractMarkwonPlugin
|
||||
import io.noties.markwon.MarkwonVisitor
|
||||
import org.commonmark.parser.Parser
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
class EmojiPlugin : AbstractMarkwonPlugin() {
|
||||
|
||||
override fun configureParser(builder: Parser.Builder) {
|
||||
builder.extensions(setOf(EmojiExtension.create()))
|
||||
}
|
||||
|
||||
override fun configureVisitor(builder: MarkwonVisitor.Builder) {
|
||||
builder.on(Emoji::class.java) { visitor, emoji ->
|
||||
val length = visitor.length()
|
||||
val emojiUnicode = emoji.emoji
|
||||
val unicode = EmojiManager.getForAlias(emoji.emoji)?.unicode
|
||||
if (!unicode.isNullOrEmpty()) {
|
||||
visitor.setSpans(length, SpannedString(unicode))
|
||||
} else {
|
||||
Timber.e(emojiUnicode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create() = EmojiPlugin()
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import android.text.SpannedString
|
||||
import com.fastaccess.markdown.emoji.EmojiManager
|
||||
import io.noties.markwon.MarkwonConfiguration
|
||||
import io.noties.markwon.Prop
|
||||
import io.noties.markwon.RenderProps
|
||||
import io.noties.markwon.SpanFactory
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Created by Kosh on 2019-07-20.
|
||||
*/
|
||||
class EmojiSpanFactory : SpanFactory {
|
||||
|
||||
override fun getSpans(
|
||||
configuration: MarkwonConfiguration,
|
||||
props: RenderProps
|
||||
): Any? {
|
||||
val emoji = props.get<Emoji>(Prop.of(":"))
|
||||
Timber.e("$props $emoji")
|
||||
if (emoji != null) {
|
||||
val unicode = EmojiManager.getForAlias(emoji.emoji)
|
||||
if (unicode?.unicode != null) {
|
||||
return SpannedString(unicode.unicode)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji.internal
|
||||
|
||||
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.Emoji
|
||||
import org.commonmark.node.Text
|
||||
import org.commonmark.parser.delimiter.DelimiterProcessor
|
||||
import org.commonmark.parser.delimiter.DelimiterRun
|
||||
|
||||
class EmojiDelimiterProcessor : DelimiterProcessor {
|
||||
|
||||
override fun getOpeningCharacter(): Char = ':'
|
||||
|
||||
override fun getClosingCharacter(): Char = ':'
|
||||
|
||||
override fun getMinLength(): Int = 1
|
||||
|
||||
override fun getDelimiterUse(
|
||||
opener: DelimiterRun,
|
||||
closer: DelimiterRun
|
||||
): Int = if (opener.length() >= 1 && closer.length() >= 1) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
override fun process(
|
||||
opener: Text,
|
||||
closer: Text,
|
||||
delimiterCount: Int
|
||||
) {
|
||||
var emoji: Emoji? = null
|
||||
val text = opener.next
|
||||
if (text is Text) {
|
||||
emoji = Emoji()
|
||||
emoji.emoji = text.literal
|
||||
text.unlink()
|
||||
}
|
||||
if (emoji != null) opener.insertAfter(emoji)
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji.internal
|
||||
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.Emoji
|
||||
import org.commonmark.node.Node
|
||||
import org.commonmark.renderer.NodeRenderer
|
||||
import org.commonmark.renderer.html.HtmlNodeRendererContext
|
||||
import org.commonmark.renderer.html.HtmlWriter
|
||||
|
||||
class EmojiNodeRenderer(private val context: HtmlNodeRendererContext) : NodeRenderer {
|
||||
private val html: HtmlWriter = context.writer
|
||||
|
||||
override fun getNodeTypes(): Set<Class<out Node>> = setOf<Class<out Node>>(Emoji::class.java)
|
||||
|
||||
override fun render(node: Node) {
|
||||
val attributes = context.extendAttributes(node, "emoji", emptyMap())
|
||||
html.tag("emoji", attributes)
|
||||
renderChildren(node)
|
||||
html.tag("/emoji")
|
||||
}
|
||||
|
||||
private fun renderChildren(parent: Node) {
|
||||
var node: Node? = parent.firstChild
|
||||
while (node != null) {
|
||||
val next = node.next
|
||||
context.render(node)
|
||||
node = next
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user