From a4615d964ffe589c73f5d0c5cb6d1c0feb27b020 Mon Sep 17 00:00:00 2001 From: Diego Beraldin Date: Sat, 9 Dec 2023 14:11:40 +0100 Subject: [PATCH] enhancement: clickable markdown and link management (#263); closes #259 * chore: cleanup markdown click event * chore: prevent clicks on markdown while handling links; closes #259 --- .../core/markdown/compose/CustomMarkdown.kt | 27 ++++++++++--------- .../markdown/plugins/ClickableImagesPlugin.kt | 2 +- .../provider/DefaultMarkwonProvider.kt | 24 ++++++++++++----- .../core/markdown/provider/MarkwonProvider.kt | 2 ++ 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/compose/CustomMarkdown.kt b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/compose/CustomMarkdown.kt index 308d76dc3..9fd839099 100755 --- a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/compose/CustomMarkdown.kt +++ b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/compose/CustomMarkdown.kt @@ -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 - onClick?.invoke() + 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 - onDoubleClick?.invoke() + 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 diff --git a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/plugins/ClickableImagesPlugin.kt b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/plugins/ClickableImagesPlugin.kt index 3abe15d47..c914440ea 100644 --- a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/plugins/ClickableImagesPlugin.kt +++ b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/plugins/ClickableImagesPlugin.kt @@ -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, diff --git a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/DefaultMarkwonProvider.kt b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/DefaultMarkwonProvider.kt index 826c4d407..68e5f48d4 100644 --- a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/DefaultMarkwonProvider.kt +++ b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/DefaultMarkwonProvider.kt @@ -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) { - plugin.addMediaDecoder(GifMediaDecoder.create(true)) - } - }, - ), + 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 + } } } }, diff --git a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/MarkwonProvider.kt b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/MarkwonProvider.kt index a0b96ddac..43834fb6f 100644 --- a/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/MarkwonProvider.kt +++ b/core-md/src/androidMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/markdown/provider/MarkwonProvider.kt @@ -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 } \ No newline at end of file