Skip to content

Commit

Permalink
Add markdown preview (#1002)
Browse files Browse the repository at this point in the history
* markdown parse the 5 lines truncated body preview in post listing for card & small card

* Fix scrollable

* Remove remnants

* Restore old stuff

---------

Co-authored-by: Vijay Ramesh <[email protected]>
Co-authored-by: Kevin Phoenix <[email protected]>
Co-authored-by: Dessalines <[email protected]>
  • Loading branch information
4 people authored Jul 10, 2023
1 parent 5ef0137 commit 59a5cbb
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 30 deletions.
17 changes: 2 additions & 15 deletions app/src/main/java/com/jerboa/ui/components/common/InputFields.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.getSelectedText
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.jerboa.R
import com.jerboa.api.uploadPictrsImage
Expand Down Expand Up @@ -580,23 +579,10 @@ fun TextMarkdownBarPreview() {
)
}

@Composable
fun PreviewLines(
text: String,
modifier: Modifier = Modifier,
) {
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
maxLines = 5,
overflow = TextOverflow.Ellipsis,
modifier = modifier,
)
}

@Composable
fun MyMarkdownText(
markdown: String,
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.onSurface,
onClick: () -> Unit,
onLongClick: (() -> Unit)? = null,
Expand All @@ -606,6 +592,7 @@ fun MyMarkdownText(
color = color,
onClick = onClick,
onLongClick = onLongClick,
modifier = modifier,
)
}

Expand Down
101 changes: 91 additions & 10 deletions app/src/main/java/com/jerboa/ui/components/common/MarkdownHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import android.content.Context
import android.os.Build
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.TextUtils
import android.text.style.URLSpan
import android.text.util.Linkify
import android.util.TypedValue
import android.view.View
import android.view.View.NOT_FOCUSABLE
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.FontRes
import androidx.annotation.IdRes
Expand All @@ -16,7 +21,7 @@ import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.toArgb
Expand Down Expand Up @@ -44,6 +49,7 @@ import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
import io.noties.markwon.ext.tables.TableAwareMovementMethod
import io.noties.markwon.ext.tables.TablePlugin
import io.noties.markwon.html.HtmlPlugin
import io.noties.markwon.html.TagHandlerNoOp
import io.noties.markwon.image.AsyncDrawableSpan
import io.noties.markwon.image.coil.CoilImagesPlugin
import io.noties.markwon.linkify.LinkifyPlugin
Expand Down Expand Up @@ -127,6 +133,7 @@ class LemmyLinkPlugin : AbstractMarkwonPlugin() {

object MarkdownHelper {
private var markwon: Markwon? = null
private var previewMarkwon: Markwon? = null

fun init(navController: NavController, useCustomTabs: Boolean, usePrivateTabs: Boolean) {
val context = navController.context
Expand All @@ -135,17 +142,18 @@ object MarkdownHelper {
.placeholder(R.drawable.ic_launcher_foreground)
.build()

// main markdown parser has coil + html on
markwon = Markwon.builder(context)
.usePlugin(CoilImagesPlugin.create(context, loader))
// email urls interfere with lemmy links
.usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS))
.usePlugin(LemmyLinkPlugin())
.usePlugin(StrikethroughPlugin.create())
.usePlugin(TablePlugin.create(context))
.usePlugin(CoilImagesPlugin.create(context, loader))
.usePlugin(HtmlPlugin.create())
// use TableAwareLinkMovementMethod to handle clicks inside tables,
// wraps LinkMovementMethod internally
.usePlugin(MovementMethodPlugin.create(TableAwareMovementMethod.create()))
.usePlugin(HtmlPlugin.create())
.usePlugin(object : AbstractMarkwonPlugin() {
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.linkResolver { _, link ->
Expand All @@ -154,24 +162,40 @@ object MarkdownHelper {
}
})
.build()

// no image parser has html off
previewMarkwon = Markwon.builder(context)
// email urls interfere with lemmy links
.usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS))
.usePlugin(LemmyLinkPlugin())
.usePlugin(StrikethroughPlugin.create())
.usePlugin(TablePlugin.create(context))
.usePlugin(HtmlPlugin.create { plugin -> plugin.addHandler(TagHandlerNoOp.create("img")) })
.usePlugin(object : AbstractMarkwonPlugin() {
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.linkResolver { _, _ -> }
}
})
.build()
}

/*
* This is a workaround for previews.
*/
fun init(context: Context) {
markwon = Markwon.builder(context).build()
previewMarkwon = Markwon.builder(context).build()
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun CreateMarkdownView(
markdown: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
onClick: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null,
style: TextStyle = MaterialTheme.typography.bodyLarge,
) {
val style = MaterialTheme.typography.bodyLarge
val defaultColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)

BoxWithConstraints {
Expand All @@ -197,11 +221,9 @@ object MarkdownHelper {
img.drawable.initWithKnownDimensions(canvasWidthMaybe, textSizeMaybe)
}
markwon!!.setParsedMarkdown(textView, md)
// if (disableLinkMovementMethod) {
// textView.movementMethod = null
// }
},
onReset = {},
modifier = modifier,
)
}
}
Expand All @@ -212,7 +234,6 @@ object MarkdownHelper {
defaultColor: Color,
fontSize: TextUnit = TextUnit.Unspecified,
textAlign: TextAlign? = null,
maxLines: Int = Int.MAX_VALUE,
@FontRes fontResource: Int? = null,
style: TextStyle,
@IdRes viewId: Int? = null,
Expand All @@ -231,7 +252,6 @@ object MarkdownHelper {
onClick?.let { setOnClickListener { onClick() } }
onLongClick?.let { setOnLongClickListener { onLongClick(); true } }
setTextColor(textColor.toArgb())
setMaxLines(maxLines)
setTextSize(TypedValue.COMPLEX_UNIT_SP, mergedStyle.fontSize.value)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setLineHeight(convertSpToPx(mergedStyle.lineHeight, context))
Expand All @@ -253,4 +273,65 @@ object MarkdownHelper {
}
}
}

@Composable
fun CreateMarkdownPreview(
markdown: String,
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.onSurface,
onClick: (() -> Unit)? = null,
style: TextStyle,
defaultColor: Color,
) {
AndroidView(
factory = { ctx ->
createTextViewPreview(
context = ctx,
color = color,
defaultColor = defaultColor,
fontSize = TextUnit.Unspecified,
style = style,
onClick = onClick,
)
},
update = { textView ->
previewMarkwon!!.setMarkdown(textView, markdown)
},
onReset = {},
modifier = modifier,
)
}

private fun createTextViewPreview(
context: Context,
color: Color = Color.Unspecified,
defaultColor: Color,
fontSize: TextUnit = TextUnit.Unspecified,
maxLines: Int = 5,
style: TextStyle,
onClick: (() -> Unit)? = null,
): TextView {
val textColor = color.takeOrElse { style.color.takeOrElse { defaultColor } }
val mergedStyle = style.merge(
TextStyle(
color = textColor,
fontSize = if (fontSize != TextUnit.Unspecified) fontSize else style.fontSize,
),
)
return TextView(context).apply {
onClick?.let { setOnClickListener { onClick() } }
setTextColor(textColor.toArgb())
setTextSize(TypedValue.COMPLEX_UNIT_SP, mergedStyle.fontSize.value)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
lineHeight = convertSpToPx(mergedStyle.lineHeight, context)
}
width = maxWidth
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
this.movementMethod = null
this.linksClickable = false
ellipsize = TextUtils.TruncateAt.END
setMaxLines(maxLines)
focusable = NOT_FOCUSABLE
}
}
}
19 changes: 14 additions & 5 deletions app/src/main/java/com/jerboa/ui/components/post/PostListing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.material.icons.outlined.Block
Expand Down Expand Up @@ -50,6 +52,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
Expand Down Expand Up @@ -89,11 +92,11 @@ import com.jerboa.ui.components.common.CommentOrPostNodeHeader
import com.jerboa.ui.components.common.DotSpacer
import com.jerboa.ui.components.common.IconAndTextDrawerItem
import com.jerboa.ui.components.common.ImageViewerDialog
import com.jerboa.ui.components.common.MarkdownHelper.CreateMarkdownPreview
import com.jerboa.ui.components.common.MyMarkdownText
import com.jerboa.ui.components.common.NsfwBadge
import com.jerboa.ui.components.common.PictrsThumbnailImage
import com.jerboa.ui.components.common.PictrsUrlImage
import com.jerboa.ui.components.common.PreviewLines
import com.jerboa.ui.components.common.ScoreAndTime
import com.jerboa.ui.components.common.SimpleTopAppBar
import com.jerboa.ui.components.common.TimeAgo
Expand Down Expand Up @@ -402,6 +405,7 @@ fun PostBody(
useCustomTabs: Boolean,
usePrivateTabs: Boolean,
blurNSFW: Boolean,
clickBody: () -> Unit = {},
) {
val post = postView.post
Column(
Expand Down Expand Up @@ -452,10 +456,14 @@ fun PostBody(
}
}
} else {
PreviewLines(
text = text,
modifier = Modifier
.padding(MEDIUM_PADDING),
val defaultColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)

CreateMarkdownPreview(
markdown = text,
defaultColor = defaultColor,
onClick = clickBody,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(MEDIUM_PADDING),
)
}
},
Expand Down Expand Up @@ -1388,6 +1396,7 @@ fun PostListingCard(
useCustomTabs = useCustomTabs,
usePrivateTabs = usePrivateTabs,
blurNSFW = blurNSFW,
clickBody = { onPostClick(postView) },
)

// Footer bar
Expand Down

0 comments on commit 59a5cbb

Please sign in to comment.