From 8a5e0f50034e52fccb2b3a932a6f197c15bdf36e Mon Sep 17 00:00:00 2001 From: Taras Smakula <tarassmakula@gmail.com> Date: Wed, 29 Nov 2023 18:46:52 +0200 Subject: [PATCH] Move makdown parsing to mapping stage --- app/build.gradle | 8 - .../circles/extensions/EditableExtensions.kt | 28 ---- .../DisplayableEventFormatter.kt | 2 +- .../timeline/list/TimelineViewHolder.kt | 7 +- .../post/create/CreatePostDialogFragment.kt | 2 +- .../post/markdown/EnhancedMovementMethod.kt | 58 ------- .../post/markdown/span/OrderedListItemSpan.kt | 56 ------- .../timeline/post/markdown/span/TextStyle.kt | 28 ---- .../org/futo/circles/view/ComposerEditText.kt | 123 --------------- .../org/futo/circles/view/PillImageSpan.kt | 141 ------------------ .../java/org/futo/circles/view/PostLayout.kt | 6 +- .../org/futo/circles/view/PreviewPostView.kt | 29 ++-- app/src/main/res/layout/view_preview_post.xml | 2 +- core/build.gradle | 8 + .../core/feature}/markdown/MarkdownParser.kt | 4 +- .../markdown/mentions/MentionsAdapter.kt | 2 +- .../markdown/mentions/MentionsPresenter.kt | 2 +- .../plugin/MentionDelimiterProcessor.kt | 2 +- .../markdown/mentions/plugin/MentionNode.kt | 2 +- .../markdown/mentions/plugin/MentionPlugin.kt | 4 +- .../feature}/markdown/span/MentionSpan.kt | 4 +- .../timeline/builder/MultiTimelineBuilder.kt | 10 +- .../timeline/builder/SingleTimelineBuilder.kt | 11 +- .../data_source/BaseTimelineDataSource.kt | 18 ++- .../timeline/post/PostContentDataSource.kt | 10 +- .../timeline/post/PostOptionsDataSource.kt | 2 +- .../core/mapping/MediaPostContentMapping.kt | 9 +- .../core/mapping/TextPostContentMapping.kt | 10 +- .../core/mapping/TimelineEventMapping.kt | 20 +-- .../futo/circles/core/model/PostContent.kt | 4 +- .../src/main/res/drawable/ic_mention.xml | 0 {app => core}/src/main/res/xml/bg_chip.xml | 0 32 files changed, 108 insertions(+), 504 deletions(-) delete mode 100644 app/src/main/java/org/futo/circles/extensions/EditableExtensions.kt delete mode 100644 app/src/main/java/org/futo/circles/feature/timeline/post/markdown/EnhancedMovementMethod.kt delete mode 100644 app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/OrderedListItemSpan.kt delete mode 100644 app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/TextStyle.kt delete mode 100644 app/src/main/java/org/futo/circles/view/ComposerEditText.kt delete mode 100644 app/src/main/java/org/futo/circles/view/PillImageSpan.kt rename {app/src/main/java/org/futo/circles/feature/timeline/post => core/src/main/java/org/futo/circles/core/feature}/markdown/MarkdownParser.kt (94%) rename {app/src/main/java/org/futo/circles/feature/timeline/post => core/src/main/java/org/futo/circles/core/feature}/markdown/mentions/MentionsAdapter.kt (91%) rename {app/src/main/java/org/futo/circles/feature/timeline/post => core/src/main/java/org/futo/circles/core/feature}/markdown/mentions/MentionsPresenter.kt (96%) rename {app/src/main/java/org/futo/circles/feature/timeline/post => core/src/main/java/org/futo/circles/core/feature}/markdown/mentions/plugin/MentionDelimiterProcessor.kt (93%) rename {app/src/main/java/org/futo/circles/feature/timeline/post => core/src/main/java/org/futo/circles/core/feature}/markdown/mentions/plugin/MentionNode.kt (72%) rename {app/src/main/java/org/futo/circles/feature/timeline/post => core/src/main/java/org/futo/circles/core/feature}/markdown/mentions/plugin/MentionPlugin.kt (88%) rename {app/src/main/java/org/futo/circles/feature/timeline/post => core/src/main/java/org/futo/circles/core/feature}/markdown/span/MentionSpan.kt (87%) rename {app => core}/src/main/res/drawable/ic_mention.xml (100%) rename {app => core}/src/main/res/xml/bg_chip.xml (100%) diff --git a/app/build.gradle b/app/build.gradle index 81f1d8a1c..85584f433 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -88,14 +88,6 @@ dependencies { //Emoji implementation 'com.vanniktech:emoji-google:0.17.0' - //Markdown - def markwon_version = "4.6.2" - implementation "io.noties.markwon:core:$markwon_version" - implementation "io.noties.markwon:linkify:$markwon_version" - implementation "io.noties.markwon:ext-strikethrough:$markwon_version" - implementation "io.noties.markwon:ext-tasklist:$markwon_version" - implementation "io.element.android:wysiwyg:2.18.0" - //Log implementation 'com.jakewharton.timber:timber:5.0.1' diff --git a/app/src/main/java/org/futo/circles/extensions/EditableExtensions.kt b/app/src/main/java/org/futo/circles/extensions/EditableExtensions.kt deleted file mode 100644 index 99b0cfcdd..000000000 --- a/app/src/main/java/org/futo/circles/extensions/EditableExtensions.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.futo.circles.extensions - -import android.text.Editable -import android.view.View -import android.view.inputmethod.InputMethodManager -import androidx.core.content.getSystemService -import org.futo.circles.feature.timeline.post.markdown.span.TextStyle -import org.futo.circles.feature.timeline.post.markdown.span.toSpanClass - -fun Editable.getGivenSpansAt( - vararg span: TextStyle, - start: Int = 0, - end: Int = length -): MutableList<Any> { - val spanList = mutableListOf<Any>() - for (selectedSpan in span) { - getSpans(start, end, selectedSpan.toSpanClass()).forEach { spanList.add(it) } - } - return spanList -} - -fun View.showKeyboard(andRequestFocus: Boolean = false) { - if (andRequestFocus) { - requestFocus() - } - val imm = context?.getSystemService<InputMethodManager>() - imm?.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT) -} \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/feature/notifications/DisplayableEventFormatter.kt b/app/src/main/java/org/futo/circles/feature/notifications/DisplayableEventFormatter.kt index ce28f3d31..dca27d5ce 100644 --- a/app/src/main/java/org/futo/circles/feature/notifications/DisplayableEventFormatter.kt +++ b/app/src/main/java/org/futo/circles/feature/notifications/DisplayableEventFormatter.kt @@ -4,7 +4,7 @@ import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import org.futo.circles.R import org.futo.circles.core.provider.MatrixSessionProvider -import org.futo.circles.feature.timeline.post.markdown.MarkdownParser +import org.futo.circles.core.feature.markdown.MarkdownParser import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel diff --git a/app/src/main/java/org/futo/circles/feature/timeline/list/TimelineViewHolder.kt b/app/src/main/java/org/futo/circles/feature/timeline/list/TimelineViewHolder.kt index d43530f63..02c0c5c03 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/list/TimelineViewHolder.kt +++ b/app/src/main/java/org/futo/circles/feature/timeline/list/TimelineViewHolder.kt @@ -21,7 +21,7 @@ import org.futo.circles.core.model.Post import org.futo.circles.core.model.TextContent import org.futo.circles.databinding.ViewPollPostBinding import org.futo.circles.databinding.ViewTextMediaPostBinding -import org.futo.circles.feature.timeline.post.markdown.MarkdownParser +import org.futo.circles.core.feature.markdown.MarkdownParser import org.futo.circles.model.* import org.futo.circles.view.PostLayout import org.futo.circles.view.PostOptionsListener @@ -31,7 +31,6 @@ sealed class PostViewHolder(view: View, private val isThread: Boolean) : RecyclerView.ViewHolder(view) { abstract val postLayout: PostLayout - protected val markwon = MarkdownParser.markwonBuilder(context) open fun bind(post: Post, userPowerLevel: Int) { postLayout.setData(post, userPowerLevel, isThread) @@ -87,7 +86,7 @@ class TextMediaPostViewHolder( private fun bindTextPost(content: TextContent) { binding.tvTextContent.apply { - setText(markwon.toMarkdown(content.message), TextView.BufferType.SPANNABLE) + setText(content.message, TextView.BufferType.SPANNABLE) visible() } binding.vMediaContent.lMedia.gone() @@ -104,7 +103,7 @@ class TextMediaPostViewHolder( binding.tvTextContent.apply { val caption = content.caption setIsVisible(caption != null) - caption?.let { setText(markwon.toMarkdown(it), TextView.BufferType.SPANNABLE) } + caption?.let { setText(it, TextView.BufferType.SPANNABLE) } } } diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt b/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt index 37b629d3a..845be5b72 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt +++ b/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt @@ -66,7 +66,7 @@ class CreatePostDialogFragment : PostContentType.IMAGE_CONTENT, PostContentType.VIDEO_CONTENT -> binding.vPostPreview.setMediaFromExistingPost(it as MediaContent) - else -> binding.vPostPreview.setText((it as TextContent).message) + else -> binding.vPostPreview.setText((it as TextContent).message.toString()) } } } diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/EnhancedMovementMethod.kt b/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/EnhancedMovementMethod.kt deleted file mode 100644 index c330c3167..000000000 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/EnhancedMovementMethod.kt +++ /dev/null @@ -1,58 +0,0 @@ -package org.futo.circles.feature.timeline.post.markdown - - -import android.text.Selection -import android.text.Spannable -import android.text.method.ArrowKeyMovementMethod -import android.text.method.MovementMethod -import android.text.style.ClickableSpan -import android.view.MotionEvent -import android.widget.TextView - - -class EnhancedMovementMethod : ArrowKeyMovementMethod() { - private var sInstance: EnhancedMovementMethod? = null - - fun getsInstance(): MovementMethod? { - if (sInstance == null) { - sInstance = EnhancedMovementMethod() - } - return sInstance - } - - override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean { - val action = event.action - if (action == MotionEvent.ACTION_UP || - action == MotionEvent.ACTION_DOWN - ) { - var x = event.x.toInt() - var y = event.y.toInt() - x -= widget.totalPaddingLeft - y -= widget.totalPaddingTop - x += widget.scrollX - y += widget.scrollY - val layout = widget.layout - val line = layout.getLineForVertical(y) - val off = layout.getOffsetForHorizontal(line, x.toFloat()) - val link = buffer.getSpans( - off, off, - ClickableSpan::class.java - ) - if (link.isNotEmpty()) { - if (action == MotionEvent.ACTION_UP) { - if (x < layout.getLineMax(0)) { - link[0].onClick(widget) - } - } else { - Selection.setSelection( - buffer, - buffer.getSpanStart(link[0]), - buffer.getSpanEnd(link[0]) - ) - } - return true - } - } - return super.onTouchEvent(widget, buffer, event) - } -} \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/OrderedListItemSpan.kt b/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/OrderedListItemSpan.kt deleted file mode 100644 index fc008d79e..000000000 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/OrderedListItemSpan.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.futo.circles.feature.timeline.post.markdown.span - -import android.graphics.Canvas -import android.graphics.Paint -import android.text.Layout -import android.text.style.LeadingMarginSpan -import io.noties.markwon.core.MarkwonTheme -import io.noties.markwon.utils.LeadingMarginUtils - - -class OrderedListItemSpan( - private val theme: MarkwonTheme, - val number: String -) : LeadingMarginSpan { - private val paint = Paint(Paint.ANTI_ALIAS_FLAG) - - private var margin = 0 - override fun getLeadingMargin(first: Boolean): Int = margin.coerceAtLeast(theme.blockMargin) - - override fun drawLeadingMargin( - c: Canvas, - p: Paint, - x: Int, - dir: Int, - top: Int, - baseline: Int, - bottom: Int, - text: CharSequence, - start: Int, - end: Int, - first: Boolean, - layout: Layout - ) { - if (!first || !LeadingMarginUtils.selfStart(start, text, this)) return - - paint.set(p) - theme.applyListItemStyle(paint) - - val numberWidth = (paint.measureText(number) + .5f).toInt() - - var width = theme.blockMargin - if (numberWidth > width) { - width = numberWidth - margin = numberWidth - } else { - margin = 0 - } - val left: Int = if (dir > 0) { - x + width * dir - numberWidth - } else { - x + width * dir + (width - numberWidth) - } - - c.drawText(number, left.toFloat(), baseline.toFloat(), paint) - } -} \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/TextStyle.kt b/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/TextStyle.kt deleted file mode 100644 index 1e06a1b9f..000000000 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/TextStyle.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.futo.circles.feature.timeline.post.markdown.span - -import android.text.style.StrikethroughSpan -import io.noties.markwon.core.spans.BulletListItemSpan -import io.noties.markwon.core.spans.EmphasisSpan -import io.noties.markwon.core.spans.LinkSpan -import io.noties.markwon.core.spans.StrongEmphasisSpan -import io.noties.markwon.ext.tasklist.TaskListSpan - -enum class TextStyle { - BOLD, - ITALIC, - STRIKE, - LINK, - UNORDERED_LIST, - ORDERED_LIST, - TASKS_LIST -} - -fun TextStyle.toSpanClass() = when (this) { - TextStyle.BOLD -> StrongEmphasisSpan::class.java - TextStyle.ITALIC -> EmphasisSpan::class.java - TextStyle.STRIKE -> StrikethroughSpan::class.java - TextStyle.LINK -> LinkSpan::class.java - TextStyle.UNORDERED_LIST -> BulletListItemSpan::class.java - TextStyle.ORDERED_LIST -> OrderedListItemSpan::class.java - TextStyle.TASKS_LIST -> TaskListSpan::class.java -} \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/view/ComposerEditText.kt b/app/src/main/java/org/futo/circles/view/ComposerEditText.kt deleted file mode 100644 index 8dba74e1c..000000000 --- a/app/src/main/java/org/futo/circles/view/ComposerEditText.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.futo.circles.view - -import android.content.Context -import android.net.Uri -import android.os.Build -import android.text.Editable -import android.util.AttributeSet -import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InputConnection -import androidx.annotation.RequiresApi -import androidx.appcompat.widget.AppCompatEditText -import androidx.core.view.ViewCompat -import androidx.core.view.inputmethod.EditorInfoCompat -import androidx.core.view.inputmethod.InputConnectionCompat -import timber.log.Timber - -class ComposerEditText @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = android.R.attr.editTextStyle -) : AppCompatEditText(context, attrs, defStyleAttr) { - - interface Callback { - fun onRichContentSelected(contentUri: Uri): Boolean - fun onTextChanged(text: CharSequence) - } - - var callback: Callback? = null - - override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection? { - var ic = super.onCreateInputConnection(editorInfo) ?: return null - val mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this) ?: arrayOf("image/*") - - EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes) - ic = InputConnectionCompat.createWrapper(this, ic, editorInfo) - - ViewCompat.setOnReceiveContentListener( - this, - mimeTypes, - UriContentListener { callback?.onRichContentSelected(it) } - ) - - return ic - } - - /** Set whether the keyboard should disable personalized learning. */ - @RequiresApi(Build.VERSION_CODES.O) - fun setUseIncognitoKeyboard(useIncognitoKeyboard: Boolean) { - imeOptions = if (useIncognitoKeyboard) { - imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING - } else { - imeOptions and EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING.inv() - } - } - - /** Set whether enter should send the message or add a new line. */ - fun setSendMessageWithEnter(sendMessageWithEnter: Boolean) { - if (sendMessageWithEnter) { - inputType = inputType and EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE.inv() - imeOptions = imeOptions or EditorInfo.IME_ACTION_SEND - } else { - inputType = inputType or EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE - imeOptions = imeOptions and EditorInfo.IME_ACTION_SEND.inv() - } - } - - init { - addTextChangedListener( - object : SimpleTextWatcher() { - var spanToRemove: PillImageSpan? = null - - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - Timber.v("Pills: beforeTextChanged: start:$start count:$count after:$after") - - if (count > after) { - // A char has been deleted - val deleteCharPosition = start + count - Timber.v("Pills: beforeTextChanged: deleted char at $deleteCharPosition") - - // Get the first span at this position - spanToRemove = editableText.getSpans(deleteCharPosition, deleteCharPosition, PillImageSpan::class.java) - .ooi { Timber.v("Pills: beforeTextChanged: found ${it.size} span(s)") } - .firstOrNull() - } - } - - override fun afterTextChanged(s: Editable) { - if (spanToRemove != null) { - val start = editableText.getSpanStart(spanToRemove) - val end = editableText.getSpanEnd(spanToRemove) - Timber.v("Pills: afterTextChanged Removing the span start:$start end:$end") - // Must be done before text replacement - editableText.removeSpan(spanToRemove) - if (start != -1 && end != -1) { - editableText.replace(start, end, "") - } - spanToRemove = null - } - callback?.onTextChanged(s.toString()) - } - } - ) - } -} - -inline fun <T> T.ooi(block: (T) -> Unit): T = also(block) diff --git a/app/src/main/java/org/futo/circles/view/PillImageSpan.kt b/app/src/main/java/org/futo/circles/view/PillImageSpan.kt deleted file mode 100644 index ba4a0f78d..000000000 --- a/app/src/main/java/org/futo/circles/view/PillImageSpan.kt +++ /dev/null @@ -1,141 +0,0 @@ -package org.futo.circles.view - -import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.Rect -import android.graphics.drawable.Drawable -import android.text.TextUtils -import android.text.style.ReplacementSpan -import android.widget.TextView -import androidx.annotation.UiThread -import androidx.appcompat.widget.ThemeUtils -import androidx.core.content.ContextCompat -import com.google.android.material.chip.ChipDrawable -import org.futo.circles.R -import org.futo.circles.core.glide.GlideRequests -import org.matrix.android.sdk.api.session.room.send.MatrixItemSpan -import org.matrix.android.sdk.api.util.MatrixItem -import java.lang.ref.WeakReference - -/** - * This span is able to replace a text by a [ChipDrawable] - * It's needed to call [bind] method to start requesting avatar, otherwise only the placeholder icon will be displayed if not already cached. - * Implements MatrixItemSpan so that it could be automatically transformed in matrix links and displayed as pills. - */ -class PillImageSpan( - private val glideRequests: GlideRequests, - private val context: Context, - override val matrixItem: MatrixItem -) : ReplacementSpan(), MatrixItemSpan { - - private val pillDrawable = createChipDrawable() - // private val target = PillImageSpanTarget(this) - private var tv: WeakReference<TextView>? = null - - @UiThread - fun bind(textView: TextView) { - tv = WeakReference(textView) - //avatarRenderer.render(glideRequests, matrixItem, target) - } - - // ReplacementSpan ***************************************************************************** - - override fun getSize( - paint: Paint, text: CharSequence, - start: Int, - end: Int, - fm: Paint.FontMetricsInt? - ): Int { - val rect = pillDrawable.bounds - if (fm != null) { - val fmPaint = paint.fontMetricsInt - val fontHeight = fmPaint.bottom - fmPaint.top - val drHeight = rect.bottom - rect.top - val top = drHeight / 2 - fontHeight / 4 - val bottom = drHeight / 2 + fontHeight / 4 - fm.ascent = -bottom - fm.top = -bottom - fm.bottom = top - fm.descent = top - } - return rect.right - } - - override fun draw( - canvas: Canvas, text: CharSequence, - start: Int, - end: Int, - x: Float, - top: Int, - y: Int, - bottom: Int, - paint: Paint - ) { - canvas.save() - val fm = paint.fontMetricsInt - val transY: Int = y + (fm.descent + fm.ascent - pillDrawable.bounds.bottom) / 2 - canvas.save() - canvas.translate(x, transY.toFloat()) - - val rect = Rect() - canvas.getClipBounds(rect) - val maxWidth = rect.right - if (pillDrawable.intrinsicWidth > maxWidth) { - pillDrawable.setBounds(0, 0, maxWidth, pillDrawable.intrinsicHeight) - pillDrawable.ellipsize = TextUtils.TruncateAt.END - } - - pillDrawable.draw(canvas) - canvas.restore() - } - - internal fun updateAvatarDrawable(drawable: Drawable?) { - pillDrawable.chipIcon = drawable - tv?.get()?.invalidate() - } - - // Private methods ***************************************************************************** - - private fun createChipDrawable(): ChipDrawable { - val textPadding = context.resources.getDimension(R.dimen.divider_height) - val icon = when { -// matrixItem is MatrixItem.RoomAliasItem && matrixItem.avatarUrl.isNullOrEmpty() && -// matrixItem.displayName == context.getString(R.string.followed_by_format, matrixItem.id) -> { -// ContextCompat.getDrawable(context, R.drawable.ic_permalink_round) -// } -// matrixItem is MatrixItem.RoomItem && matrixItem.avatarUrl.isNullOrEmpty() && ( -// matrixItem.displayName == context.getString(R.string.pill_message_in_unknown_room) || -// matrixItem.displayName == context.getString(R.string.pill_message_unknown_room_or_space) || -// matrixItem.displayName == context.getString(R.string.pill_message_from_unknown_user) -// ) -> { -// ContextCompat.getDrawable(context, R.drawable.ic_composer_bold) -// } - matrixItem is MatrixItem.UserItem && matrixItem.avatarUrl.isNullOrEmpty() -> { - ContextCompat.getDrawable(context, R.drawable.ic_composer_bold) - } - else -> { - try { - // avatarRenderer.getCachedDrawable(glideRequests, matrixItem) - } catch (exception: Exception) { - // avatarRenderer.getPlaceholderDrawable(matrixItem) - } - } - } - - return ChipDrawable.createFromResource(context, R.xml.bg_chip).apply { - text = "matrixItem.getBestName()" - textEndPadding = textPadding - textStartPadding = textPadding - setChipMinHeightResource(R.dimen.rich_text_composer_corner_radius_expanded) - setChipIconSizeResource(R.dimen.rich_text_composer_corner_radius_expanded) - //chipIcon = icon - if (matrixItem is MatrixItem.EveryoneInRoomItem) { - // setTextColor API does not exist right now for ChipDrawable, use textAppearance - } - setBounds(0, 0, intrinsicWidth, intrinsicHeight) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/view/PostLayout.kt b/app/src/main/java/org/futo/circles/view/PostLayout.kt index 40f74e3a9..808ffc9dd 100644 --- a/app/src/main/java/org/futo/circles/view/PostLayout.kt +++ b/app/src/main/java/org/futo/circles/view/PostLayout.kt @@ -17,7 +17,7 @@ import org.futo.circles.core.model.Post import org.futo.circles.core.model.PostContent import org.futo.circles.core.model.TextContent import org.futo.circles.databinding.LayoutPostBinding -import org.futo.circles.feature.timeline.post.markdown.MarkdownParser +import org.futo.circles.core.feature.markdown.MarkdownParser import org.futo.circles.model.PostItemPayload import org.matrix.android.sdk.api.session.room.send.SendState @@ -114,10 +114,10 @@ class PostLayout( private fun setMentionBorder(content: PostContent) { val hasMention = when (content) { is MediaContent -> content.caption?.let { - MarkdownParser.hasCurrentUserMention(it) + MarkdownParser.hasCurrentUserMention(it.toString()) } ?: false - is TextContent -> MarkdownParser.hasCurrentUserMention(content.message) + is TextContent -> MarkdownParser.hasCurrentUserMention(content.message.toString()) is PollContent -> false } if (hasMention) binding.lCard.setBackgroundResource(R.drawable.bg_mention_highlight) diff --git a/app/src/main/java/org/futo/circles/view/PreviewPostView.kt b/app/src/main/java/org/futo/circles/view/PreviewPostView.kt index 785cb745d..7b9946bd3 100644 --- a/app/src/main/java/org/futo/circles/view/PreviewPostView.kt +++ b/app/src/main/java/org/futo/circles/view/PreviewPostView.kt @@ -6,10 +6,12 @@ import android.content.Context import android.graphics.drawable.ColorDrawable import android.net.Uri import android.text.Editable +import android.text.Spanned import android.util.AttributeSet import android.view.LayoutInflater import android.view.MotionEvent import android.view.View +import android.view.inputmethod.InputMethodManager import android.widget.LinearLayout import androidx.annotation.DrawableRes import androidx.cardview.widget.CardView @@ -39,9 +41,9 @@ import org.futo.circles.core.utils.VideoUtils.getVideoDurationString import org.futo.circles.databinding.ViewPreviewPostBinding import org.futo.circles.databinding.ViewRichTextMenuButtonBinding import org.futo.circles.extensions.convertDpToPixel -import org.futo.circles.extensions.showKeyboard import org.futo.circles.feature.timeline.post.create.PreviewPostListener -import org.futo.circles.feature.timeline.post.markdown.mentions.MentionsPresenter +import org.futo.circles.core.feature.markdown.mentions.MentionsPresenter +import org.futo.circles.core.feature.markdown.span.MentionSpan import org.futo.circles.model.CreatePostContent import org.futo.circles.model.MediaPostContent import org.futo.circles.model.TextPostContent @@ -87,8 +89,6 @@ class PreviewPostView( doAfterTextChanged { binding.btnSend.isEnabled = it?.toString()?.isNotBlank() == true } - setShadowLayer(paddingBottom.toFloat(), 0f, 0f, 0) - disallowParentInterceptTouchEvent(this) } setupRichTextMenu() } @@ -130,10 +130,10 @@ class PreviewPostView( fun setMediaFromExistingPost(mediaContent: MediaContent) { canEditMedia = false val caption = mediaContent.caption ?: "" - setText(caption) + setText(caption.toString()) val uri = Uri.parse(mediaContent.mediaFileData.fileUrl) val mediaType = mediaContent.getMediaType() - postContent = MediaPostContent(caption, uri, mediaType) + postContent = MediaPostContent(caption.toString(), uri, mediaType) updateContentView() loadMediaCover(mediaContent) val isVideo = mediaType == MediaType.Video @@ -235,14 +235,19 @@ class PreviewPostView( } private fun requestFocusOnText() { - with(binding.etTextPost) { - this.post { - showKeyboard(true) - text?.length?.let { setSelection(it) } - } + binding.etTextPost.post { + requestFocus() + binding.etTextPost.text?.let { binding.etTextPost.setSelection(it.length) } + showKeyboard() } } + private fun showKeyboard() { + val inputMethodManager: InputMethodManager = + context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.showSoftInput(binding.etTextPost, 0) + } + private fun getMyUser(): User? { val session = MatrixSessionProvider.currentSession return session?.myUserId?.let { session.getUser(it) } @@ -257,7 +262,7 @@ class PreviewPostView( addMenuItem(binding.lMainMenu, R.drawable.ic_emoji) { listener?.onEmojiClicked() } - addMenuItem(binding.lMainMenu, R.drawable.ic_mention) { + addMenuItem(binding.lMainMenu, org.futo.circles.core.R.drawable.ic_mention) { binding.etTextPost.append("@") } addMenuItem(binding.lMainMenu, R.drawable.ic_link) { diff --git a/app/src/main/res/layout/view_preview_post.xml b/app/src/main/res/layout/view_preview_post.xml index 858792b93..f7fafaf9e 100644 --- a/app/src/main/res/layout/view_preview_post.xml +++ b/app/src/main/res/layout/view_preview_post.xml @@ -61,7 +61,7 @@ android:hint="@string/enter_your_message_here" app:bulletGap="8sp" app:bulletRadius="4sp" - app:codeBlockBackgroundDrawable="@drawable/bg_code_block" + app:pillBackgroundColor="@color/white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/core/build.gradle b/core/build.gradle index cca12aac7..eaf7e8a60 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -95,6 +95,14 @@ dependencies { //QR api 'com.google.zxing:core:3.5.2' + //Markdown + def markwon_version = "4.6.2" + api "io.noties.markwon:core:$markwon_version" + api "io.noties.markwon:linkify:$markwon_version" + api "io.noties.markwon:ext-strikethrough:$markwon_version" + api "io.noties.markwon:ext-tasklist:$markwon_version" + api "io.element.android:wysiwyg:2.18.0" + //Shake detection implementation 'com.squareup:seismic:1.0.3' diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/MarkdownParser.kt b/core/src/main/java/org/futo/circles/core/feature/markdown/MarkdownParser.kt similarity index 94% rename from app/src/main/java/org/futo/circles/feature/timeline/post/markdown/MarkdownParser.kt rename to core/src/main/java/org/futo/circles/core/feature/markdown/MarkdownParser.kt index b867c69b9..38040c8b6 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/MarkdownParser.kt +++ b/core/src/main/java/org/futo/circles/core/feature/markdown/MarkdownParser.kt @@ -1,4 +1,4 @@ -package org.futo.circles.feature.timeline.post.markdown +package org.futo.circles.core.feature.markdown import android.content.Context import android.graphics.Typeface @@ -13,7 +13,7 @@ import org.commonmark.node.Emphasis import org.commonmark.node.StrongEmphasis import org.futo.circles.core.extensions.notEmptyDisplayName import org.futo.circles.core.provider.MatrixSessionProvider -import org.futo.circles.feature.timeline.post.markdown.mentions.plugin.MentionPlugin +import org.futo.circles.core.feature.markdown.mentions.plugin.MentionPlugin import org.matrix.android.sdk.api.session.getUserOrDefault diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/MentionsAdapter.kt b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/MentionsAdapter.kt similarity index 91% rename from app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/MentionsAdapter.kt rename to core/src/main/java/org/futo/circles/core/feature/markdown/mentions/MentionsAdapter.kt index f2d8de199..43a4416fc 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/MentionsAdapter.kt +++ b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/MentionsAdapter.kt @@ -1,4 +1,4 @@ -package org.futo.circles.feature.timeline.post.markdown.mentions +package org.futo.circles.core.feature.markdown.mentions import android.view.ViewGroup import org.futo.circles.core.base.list.BaseRvAdapter diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/MentionsPresenter.kt b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/MentionsPresenter.kt similarity index 96% rename from app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/MentionsPresenter.kt rename to core/src/main/java/org/futo/circles/core/feature/markdown/mentions/MentionsPresenter.kt index 1024ed548..bedc93b4a 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/MentionsPresenter.kt +++ b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/MentionsPresenter.kt @@ -1,4 +1,4 @@ -package org.futo.circles.feature.timeline.post.markdown.mentions +package org.futo.circles.core.feature.markdown.mentions import android.content.Context import androidx.recyclerview.widget.RecyclerView diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionDelimiterProcessor.kt b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionDelimiterProcessor.kt similarity index 93% rename from app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionDelimiterProcessor.kt rename to core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionDelimiterProcessor.kt index 66c2bbf46..817dc7d4d 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionDelimiterProcessor.kt +++ b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionDelimiterProcessor.kt @@ -1,4 +1,4 @@ -package org.futo.circles.feature.timeline.post.markdown.mentions.plugin +package org.futo.circles.core.feature.markdown.mentions.plugin import org.commonmark.node.Node import org.commonmark.node.Text diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionNode.kt b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionNode.kt similarity index 72% rename from app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionNode.kt rename to core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionNode.kt index 4e0274255..6aca6a1a2 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionNode.kt +++ b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionNode.kt @@ -1,4 +1,4 @@ -package org.futo.circles.feature.timeline.post.markdown.mentions.plugin +package org.futo.circles.core.feature.markdown.mentions.plugin import org.commonmark.node.CustomNode import org.commonmark.node.Visitor diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionPlugin.kt b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionPlugin.kt similarity index 88% rename from app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionPlugin.kt rename to core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionPlugin.kt index 797fc787e..2a2b1ff6b 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/mentions/plugin/MentionPlugin.kt +++ b/core/src/main/java/org/futo/circles/core/feature/markdown/mentions/plugin/MentionPlugin.kt @@ -1,11 +1,11 @@ -package org.futo.circles.feature.timeline.post.markdown.mentions.plugin +package org.futo.circles.core.feature.markdown.mentions.plugin import android.content.Context import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.MarkwonVisitor import io.noties.markwon.SpannableBuilder import org.commonmark.parser.Parser -import org.futo.circles.feature.timeline.post.markdown.span.MentionSpan +import org.futo.circles.core.feature.markdown.span.MentionSpan class MentionPlugin(private val context: Context) : AbstractMarkwonPlugin() { diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/MentionSpan.kt b/core/src/main/java/org/futo/circles/core/feature/markdown/span/MentionSpan.kt similarity index 87% rename from app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/MentionSpan.kt rename to core/src/main/java/org/futo/circles/core/feature/markdown/span/MentionSpan.kt index 04f900ea4..bb3313815 100644 --- a/app/src/main/java/org/futo/circles/feature/timeline/post/markdown/span/MentionSpan.kt +++ b/core/src/main/java/org/futo/circles/core/feature/markdown/span/MentionSpan.kt @@ -1,11 +1,11 @@ -package org.futo.circles.feature.timeline.post.markdown.span +package org.futo.circles.core.feature.markdown.span import android.content.Context import android.graphics.drawable.Drawable import android.text.style.DynamicDrawableSpan import androidx.core.content.ContextCompat import com.google.android.material.chip.ChipDrawable -import org.futo.circles.R +import org.futo.circles.core.R class MentionSpan( private val context: Context, diff --git a/core/src/main/java/org/futo/circles/core/feature/timeline/builder/MultiTimelineBuilder.kt b/core/src/main/java/org/futo/circles/core/feature/timeline/builder/MultiTimelineBuilder.kt index 1e9d80c71..fd857635f 100644 --- a/core/src/main/java/org/futo/circles/core/feature/timeline/builder/MultiTimelineBuilder.kt +++ b/core/src/main/java/org/futo/circles/core/feature/timeline/builder/MultiTimelineBuilder.kt @@ -1,5 +1,8 @@ package org.futo.circles.core.feature.timeline.builder +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import org.futo.circles.core.feature.markdown.MarkdownParser import org.futo.circles.core.mapping.toPost import org.futo.circles.core.model.Post import org.futo.circles.core.provider.MatrixSessionProvider @@ -7,16 +10,19 @@ import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject -class MultiTimelineBuilder @Inject constructor() : BaseTimelineBuilder() { +class MultiTimelineBuilder @Inject constructor( + @ApplicationContext context: Context +) : BaseTimelineBuilder() { private var currentSnapshotMap: MutableMap<String, List<Post>> = mutableMapOf() private var readReceiptMap: MutableMap<String, List<Long>> = mutableMapOf() + private val markwon = MarkdownParser.markwonBuilder(context) override fun List<TimelineEvent>.processSnapshot(isThread: Boolean): List<Post> { val roomId = firstOrNull()?.roomId ?: return emptyList() val room = MatrixSessionProvider.currentSession?.getRoom(roomId) ?: return emptyList() val receipts = getReadReceipts(room).also { readReceiptMap[roomId] = it } - currentSnapshotMap[roomId] = this.map { it.toPost(receipts) } + currentSnapshotMap[roomId] = this.map { it.toPost(markwon, receipts) } val fullTimelineEventList = currentSnapshotMap.flatMap { (_, value) -> value } return sortList(fullTimelineEventList, isThread) } diff --git a/core/src/main/java/org/futo/circles/core/feature/timeline/builder/SingleTimelineBuilder.kt b/core/src/main/java/org/futo/circles/core/feature/timeline/builder/SingleTimelineBuilder.kt index 019b399b1..9684e294a 100644 --- a/core/src/main/java/org/futo/circles/core/feature/timeline/builder/SingleTimelineBuilder.kt +++ b/core/src/main/java/org/futo/circles/core/feature/timeline/builder/SingleTimelineBuilder.kt @@ -1,5 +1,8 @@ package org.futo.circles.core.feature.timeline.builder +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import org.futo.circles.core.feature.markdown.MarkdownParser import org.futo.circles.core.mapping.toPost import org.futo.circles.core.model.Post import org.futo.circles.core.provider.MatrixSessionProvider @@ -7,13 +10,17 @@ import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject -class SingleTimelineBuilder @Inject constructor() : BaseTimelineBuilder() { +class SingleTimelineBuilder @Inject constructor( + @ApplicationContext context: Context +) : BaseTimelineBuilder() { + + private val markwon = MarkdownParser.markwonBuilder(context) override fun List<TimelineEvent>.processSnapshot(isThread: Boolean): List<Post> { val room = MatrixSessionProvider.currentSession?.getRoom(firstOrNull()?.roomId ?: "") ?: return emptyList() val receipts = getReadReceipts(room) - return sortList(this.map { it.toPost(receipts) }, isThread) + return sortList(this.map { it.toPost(markwon, receipts) }, isThread) } } \ No newline at end of file diff --git a/core/src/main/java/org/futo/circles/core/feature/timeline/data_source/BaseTimelineDataSource.kt b/core/src/main/java/org/futo/circles/core/feature/timeline/data_source/BaseTimelineDataSource.kt index 82715bea1..79975d7b5 100644 --- a/core/src/main/java/org/futo/circles/core/feature/timeline/data_source/BaseTimelineDataSource.kt +++ b/core/src/main/java/org/futo/circles/core/feature/timeline/data_source/BaseTimelineDataSource.kt @@ -1,13 +1,15 @@ package org.futo.circles.core.feature.timeline.data_source +import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.qualifiers.ApplicationContext import org.futo.circles.core.extensions.getOrThrow -import org.futo.circles.core.model.Post -import org.futo.circles.core.provider.MatrixSessionProvider import org.futo.circles.core.feature.timeline.builder.BaseTimelineBuilder import org.futo.circles.core.feature.timeline.builder.MultiTimelineBuilder import org.futo.circles.core.feature.timeline.builder.SingleTimelineBuilder +import org.futo.circles.core.model.Post +import org.futo.circles.core.provider.MatrixSessionProvider import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.timeline.Timeline @@ -21,10 +23,16 @@ abstract class BaseTimelineDataSource( private val timelineBuilder: BaseTimelineBuilder ) : Timeline.Listener { - class Factory @Inject constructor(private val savedStateHandle: SavedStateHandle) { + class Factory @Inject constructor( + private val savedStateHandle: SavedStateHandle, + @ApplicationContext private val context: Context + ) { fun create(isMultiTimelines: Boolean): BaseTimelineDataSource = - if (isMultiTimelines) MultiTimelinesDataSource(savedStateHandle, MultiTimelineBuilder()) - else SingleTimelineDataSource(savedStateHandle, SingleTimelineBuilder()) + if (isMultiTimelines) MultiTimelinesDataSource( + savedStateHandle, + MultiTimelineBuilder(context) + ) + else SingleTimelineDataSource(savedStateHandle, SingleTimelineBuilder(context)) } protected val roomId: String = savedStateHandle.getOrThrow("roomId") diff --git a/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostContentDataSource.kt b/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostContentDataSource.kt index aae616280..e8442ebb6 100644 --- a/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostContentDataSource.kt +++ b/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostContentDataSource.kt @@ -1,6 +1,9 @@ package org.futo.circles.core.feature.timeline.post +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ViewModelScoped +import org.futo.circles.core.feature.markdown.MarkdownParser import org.futo.circles.core.mapping.toPost import org.futo.circles.core.model.Post import org.futo.circles.core.model.PostContent @@ -11,14 +14,17 @@ import javax.inject.Inject @ViewModelScoped -class PostContentDataSource @Inject constructor() { +class PostContentDataSource @Inject constructor( + @ApplicationContext context: Context +) { private val session = MatrixSessionProvider.currentSession + private val markwon = MarkdownParser.markwonBuilder(context) fun getPost(roomId: String, eventId: String): Post? { val roomForMessage = session?.getRoom(roomId) val timelineEvent = roomForMessage?.getTimelineEvent(eventId) ?: return null - return timelineEvent.toPost() + return timelineEvent.toPost(markwon) } fun getPostContent(roomId: String, eventId: String): PostContent? = diff --git a/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostOptionsDataSource.kt b/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostOptionsDataSource.kt index 7db832ffd..bae01ac4f 100644 --- a/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostOptionsDataSource.kt +++ b/core/src/main/java/org/futo/circles/core/feature/timeline/post/PostOptionsDataSource.kt @@ -44,7 +44,7 @@ class PostOptionsDataSource @Inject constructor( suspend fun getShareableContent(content: PostContent): ShareableContent? = onBG { when (content) { is MediaContent -> getShareableMediaContent(content.mediaFileData) - is TextContent -> TextShareable(content.message) + is TextContent -> TextShareable(content.message.toString()) else -> throw IllegalArgumentException("Not shareable post content") } } diff --git a/core/src/main/java/org/futo/circles/core/mapping/MediaPostContentMapping.kt b/core/src/main/java/org/futo/circles/core/mapping/MediaPostContentMapping.kt index d6aeb3833..36b694c50 100644 --- a/core/src/main/java/org/futo/circles/core/mapping/MediaPostContentMapping.kt +++ b/core/src/main/java/org/futo/circles/core/mapping/MediaPostContentMapping.kt @@ -1,6 +1,7 @@ package org.futo.circles.core.mapping import com.bumptech.glide.request.target.Target +import io.noties.markwon.Markwon import org.futo.circles.core.base.MediaCaptionFieldKey import org.futo.circles.core.model.MediaContent import org.futo.circles.core.model.MediaFileData @@ -15,18 +16,18 @@ import org.matrix.android.sdk.api.session.room.model.message.getFileName import org.matrix.android.sdk.api.session.room.model.message.getFileUrl import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -fun TimelineEvent.toMediaContent(mediaType: MediaType): MediaContent = MediaContent( +fun TimelineEvent.toMediaContent(mediaType: MediaType, markwon: Markwon): MediaContent = MediaContent( type = if (mediaType == MediaType.Image) PostContentType.IMAGE_CONTENT else PostContentType.VIDEO_CONTENT, - caption = getCaption(), + caption = getCaption(markwon), mediaFileData = toMediaFileData(mediaType), thumbnailFileData = toThumbnailFileData(mediaType), thumbHash = getThumbHash(mediaType) ) -private fun TimelineEvent.getCaption(): String? { +private fun TimelineEvent.getCaption(markwon: Markwon): CharSequence? { val lastContent = annotations?.editSummary?.latestEdit?.getClearContent() ?: root.getClearContent() - return lastContent?.get(MediaCaptionFieldKey)?.toString() + return lastContent?.get(MediaCaptionFieldKey)?.toString()?.let { markwon.toMarkdown(it) } } private fun TimelineEvent.getThumbHash(mediaType: MediaType) = when (mediaType) { diff --git a/core/src/main/java/org/futo/circles/core/mapping/TextPostContentMapping.kt b/core/src/main/java/org/futo/circles/core/mapping/TextPostContentMapping.kt index a8104f4eb..95634a367 100644 --- a/core/src/main/java/org/futo/circles/core/mapping/TextPostContentMapping.kt +++ b/core/src/main/java/org/futo/circles/core/mapping/TextPostContentMapping.kt @@ -1,9 +1,13 @@ package org.futo.circles.core.mapping +import io.noties.markwon.Markwon import org.futo.circles.core.model.TextContent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent -fun TimelineEvent.toTextContent(): TextContent = TextContent( - message = getTextEditableContent(false) -) \ No newline at end of file +fun TimelineEvent.toTextContent(markwon: Markwon): TextContent = TextContent( + message = markwon.toMarkdown(getTextEditableContent(false)) + +) + + diff --git a/core/src/main/java/org/futo/circles/core/mapping/TimelineEventMapping.kt b/core/src/main/java/org/futo/circles/core/mapping/TimelineEventMapping.kt index c024e6572..8a5d87b14 100644 --- a/core/src/main/java/org/futo/circles/core/mapping/TimelineEventMapping.kt +++ b/core/src/main/java/org/futo/circles/core/mapping/TimelineEventMapping.kt @@ -1,5 +1,6 @@ package org.futo.circles.core.mapping +import io.noties.markwon.Markwon import org.futo.circles.core.extensions.getPostContentType import org.futo.circles.core.model.MediaType import org.futo.circles.core.model.Post @@ -10,9 +11,9 @@ import org.futo.circles.core.model.ReactionsData import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited -fun TimelineEvent.toPost(readReceipts: List<Long> = emptyList()): Post = Post( +fun TimelineEvent.toPost(markwon: Markwon, readReceipts: List<Long> = emptyList()): Post = Post( postInfo = toPostInfo(), - content = toPostContent(), + content = toPostContent(markwon), sendState = root.sendState, readByCount = getReadByCount(readReceipts), repliesCount = root.threadDetails?.numberOfThreads ?: 0, @@ -30,13 +31,14 @@ private fun TimelineEvent.toPostInfo(): PostInfo = PostInfo( isEdited = hasBeenEdited() ) -private fun TimelineEvent.toPostContent(): PostContent = when (getPostContentType()) { - PostContentType.TEXT_CONTENT -> toTextContent() - PostContentType.IMAGE_CONTENT -> toMediaContent(MediaType.Image) - PostContentType.VIDEO_CONTENT -> toMediaContent(MediaType.Video) - PostContentType.POLL_CONTENT -> toPollContent() - else -> toTextContent() -} +private fun TimelineEvent.toPostContent(markwon: Markwon): PostContent = + when (getPostContentType()) { + PostContentType.TEXT_CONTENT -> toTextContent(markwon) + PostContentType.IMAGE_CONTENT -> toMediaContent(MediaType.Image, markwon) + PostContentType.VIDEO_CONTENT -> toMediaContent(MediaType.Video, markwon) + PostContentType.POLL_CONTENT -> toPollContent() + else -> toTextContent(markwon) + } private fun TimelineEvent.getReadByCount(receipts: List<Long>): Int { val eventTime = root.originServerTs ?: 0 diff --git a/core/src/main/java/org/futo/circles/core/model/PostContent.kt b/core/src/main/java/org/futo/circles/core/model/PostContent.kt index 7fd70f87f..8db795efd 100644 --- a/core/src/main/java/org/futo/circles/core/model/PostContent.kt +++ b/core/src/main/java/org/futo/circles/core/model/PostContent.kt @@ -18,12 +18,12 @@ sealed class PostContent(open val type: PostContentType) { } data class TextContent( - val message: String + val message: CharSequence ) : PostContent(PostContentType.TEXT_CONTENT) data class MediaContent( override val type: PostContentType, - val caption: String?, + val caption: CharSequence?, val mediaFileData: MediaFileData, val thumbnailFileData: MediaFileData?, val thumbHash: String? diff --git a/app/src/main/res/drawable/ic_mention.xml b/core/src/main/res/drawable/ic_mention.xml similarity index 100% rename from app/src/main/res/drawable/ic_mention.xml rename to core/src/main/res/drawable/ic_mention.xml diff --git a/app/src/main/res/xml/bg_chip.xml b/core/src/main/res/xml/bg_chip.xml similarity index 100% rename from app/src/main/res/xml/bg_chip.xml rename to core/src/main/res/xml/bg_chip.xml -- GitLab