enhancement: clickable markdown and link management (#263); closes #259

* chore: cleanup markdown click event

* chore: prevent clicks on markdown while handling links; closes #259
This commit is contained in:
Diego Beraldin 2023-12-09 14:11:40 +01:00 committed by GitHub
parent 239aecb83c
commit a4615d964f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 20 deletions

View File

@ -34,7 +34,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownColo
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownPadding
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.MarkdownTypography
import com.github.diegoberaldin.raccoonforlemmy.core.markdown.model.ReferenceLinkHandlerImpl
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.DateTime
import io.noties.markwon.image.AsyncDrawableSpan
import org.intellij.markdown.flavours.MarkdownFlavourDescriptor
@ -75,10 +74,7 @@ actual fun CustomMarkdown(
)
}
BoxWithConstraints(
modifier = modifier.onClick(
onClick = onClick ?: {},
onDoubleClick = onDoubleClick ?: {},
)
modifier = modifier
) {
val style = LocalMarkdownTypography.current.text
val fontScale = LocalDensity.current.fontScale * 1.25f
@ -116,7 +112,10 @@ actual fun CustomMarkdown(
val currentTime = DateTime.epochMillis()
if ((currentTime - lastClickTime) < 300) return false
lastClickTime = currentTime
if (!markwonProvider.isHandlingLink.value) {
cancelPendingInputEvents()
onClick?.invoke()
}
return true
}
@ -124,7 +123,10 @@ actual fun CustomMarkdown(
val currentTime = DateTime.epochMillis()
if ((currentTime - lastClickTime) < 300) return false
lastClickTime = currentTime
if (!markwonProvider.isHandlingLink.value) {
cancelPendingInputEvents()
onDoubleClick?.invoke()
}
return true
}
@ -133,7 +135,12 @@ actual fun CustomMarkdown(
}
}
)
setOnTouchListener { _, evt -> gestureDetector.onTouchEvent(evt) }
setOnTouchListener { v, evt ->
if (evt.action == MotionEvent.ACTION_UP) {
v.performClick()
}
gestureDetector.onTouchEvent(evt)
}
}
},
update = { textView ->
@ -157,8 +164,6 @@ private fun createTextView(
typeface: Typeface? = null,
style: TextStyle,
@IdRes viewId: Int? = null,
onClick: (() -> Unit)? = null,
onLongClick: ((View) -> Boolean)? = null,
): TextView {
val mergedStyle = style.merge(
TextStyle(
@ -168,8 +173,6 @@ private fun createTextView(
),
)
return TextView(context).apply {
onClick?.let { setOnClickListener { onClick() } }
onLongClick?.let { setOnLongClickListener(it) }
setTextColor(textColor.toArgb())
setTextSize(TypedValue.COMPLEX_UNIT_SP, mergedStyle.fontSize.value)
width = maxWidth

View File

@ -14,7 +14,7 @@ import kotlinx.coroutines.launch
import org.commonmark.node.Image
import org.commonmark.node.Node
private const val TRIGGER_UPDATE_INTERVAL = 250L
private const val TRIGGER_UPDATE_INTERVAL = 500L
class ClickableImagesPlugin private constructor(
private val context: Context,

View File

@ -14,6 +14,12 @@ import io.noties.markwon.html.HtmlPlugin
import io.noties.markwon.image.ImagesPlugin
import io.noties.markwon.image.gif.GifMediaDecoder
import io.noties.markwon.linkify.LinkifyPlugin
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class DefaultMarkwonProvider(
context: Context,
@ -21,7 +27,10 @@ class DefaultMarkwonProvider(
onOpenImage: ((String) -> Unit)?,
onImageTriggerUpdate: (() -> Unit)?,
) : MarkwonProvider {
override val markwon: Markwon
override val isHandlingLink = MutableStateFlow(false)
private val scope = CoroutineScope(SupervisorJob())
init {
markwon = Markwon.builder(context)
@ -31,13 +40,9 @@ class DefaultMarkwonProvider(
.usePlugin(TablePlugin.create(context))
.usePlugin(HtmlPlugin.create())
.usePlugin(
ImagesPlugin.create(
object : ImagesPlugin.ImagesConfigure {
override fun configureImages(plugin: ImagesPlugin) {
ImagesPlugin.create { plugin ->
plugin.addMediaDecoder(GifMediaDecoder.create(true))
}
},
),
)
.usePlugin(MarkwonSpoilerPlugin.create(true))
.usePlugin(
@ -53,7 +58,12 @@ class DefaultMarkwonProvider(
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.linkResolver { view, link ->
view.cancelPendingInputEvents()
isHandlingLink.value = true
onOpenUrl?.invoke(link)
scope.launch {
delay(300)
isHandlingLink.value = false
}
}
}
},

View File

@ -1,7 +1,9 @@
package com.github.diegoberaldin.raccoonforlemmy.core.markdown.provider
import io.noties.markwon.Markwon
import kotlinx.coroutines.flow.StateFlow
interface MarkwonProvider {
val markwon: Markwon
val isHandlingLink: StateFlow<Boolean>
}