feat(markdown): support timestamps
Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
10a40f75e7
commit
33fd3c6bd4
|
|
@ -29,6 +29,7 @@ import chat.revolt.internals.markdown.CustomEmoteRule
|
|||
import chat.revolt.internals.markdown.MarkdownContext
|
||||
import chat.revolt.internals.markdown.MarkdownParser
|
||||
import chat.revolt.internals.markdown.MarkdownState
|
||||
import chat.revolt.internals.markdown.TimestampRule
|
||||
import chat.revolt.internals.markdown.UserMentionRule
|
||||
import chat.revolt.internals.markdown.createCodeRule
|
||||
import chat.revolt.internals.markdown.createInlineCodeRule
|
||||
|
|
@ -61,6 +62,7 @@ fun UIMarkdown(
|
|||
UserMentionRule(),
|
||||
ChannelMentionRule(),
|
||||
CustomEmoteRule(),
|
||||
TimestampRule(),
|
||||
)
|
||||
.addRules(
|
||||
createCodeRule(context, codeBlockColor.toArgb()),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
package chat.revolt.internals.markdown
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import com.discord.simpleast.core.node.Node
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.toJavaInstant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
|
||||
class UserMentionNode(private val userId: String) : Node<MarkdownContext>() {
|
||||
override fun render(builder: SpannableStringBuilder, renderContext: MarkdownContext) {
|
||||
|
|
@ -30,3 +38,67 @@ class CustomEmoteNode(private val emoteId: String) : Node<MarkdownContext>() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TimestampNode(private val timestamp: Long, private val modifier: String? = null) :
|
||||
Node<MarkdownContext>() {
|
||||
override fun render(builder: SpannableStringBuilder, renderContext: MarkdownContext) {
|
||||
val normalisedModifier = modifier.orEmpty().removePrefix(":")
|
||||
|
||||
val instant = Instant.fromEpochSeconds(timestamp)
|
||||
val javaInstant = instant.toJavaInstant()
|
||||
|
||||
try {
|
||||
if (timestamp < 0) {
|
||||
builder.append("<invalid timestamp>")
|
||||
return
|
||||
}
|
||||
|
||||
val outString = when (normalisedModifier) {
|
||||
// 22:22
|
||||
"t" -> DateTimeFormatter.ofPattern("HH:mm")
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault())
|
||||
.format(javaInstant)
|
||||
|
||||
// 22:22:22
|
||||
"T" -> DateTimeFormatter.ofPattern("HH:mm:ss")
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault())
|
||||
.format(javaInstant)
|
||||
|
||||
// 22 September 2022
|
||||
"D" -> DateTimeFormatter.ofPattern("dd MMMM yyyy")
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault())
|
||||
.format(javaInstant)
|
||||
|
||||
// 22 September 2022 22:22
|
||||
"f" -> DateTimeFormatter.ofPattern("dd MMMM yyyy HH:mm")
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault())
|
||||
.format(javaInstant)
|
||||
|
||||
// Thursday, 22 September 2022 22:22
|
||||
"F" -> DateTimeFormatter.ofPattern("EEEE, dd MMMM yyyy HH:mm")
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault())
|
||||
.format(javaInstant)
|
||||
|
||||
// 9 months ago
|
||||
"R" -> DateUtils.getRelativeTimeSpanString(
|
||||
timestamp * 1000,
|
||||
Clock.System.now().toEpochMilliseconds(),
|
||||
DateUtils.MINUTE_IN_MILLIS
|
||||
)
|
||||
|
||||
// Fallback. Shouldn't happen
|
||||
else -> timestamp.toString()
|
||||
}
|
||||
|
||||
builder.append(outString)
|
||||
} catch (e: Exception) {
|
||||
Log.e("TimestampNode", "Failed to parse timestamp", e)
|
||||
builder.append("<invalid timestamp>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,26 @@ class CustomEmoteRule<S> :
|
|||
}
|
||||
}
|
||||
|
||||
class TimestampRule<S> :
|
||||
Rule<MarkdownContext, TimestampNode, S>(Pattern.compile("^<t:([0-9]+?)(:[tTDfFR])?>")) {
|
||||
override fun parse(
|
||||
matcher: Matcher,
|
||||
parser: Parser<MarkdownContext, in TimestampNode, S>,
|
||||
state: S
|
||||
): ParseSpec<MarkdownContext, S> {
|
||||
return ParseSpec.createTerminal(
|
||||
TimestampNode(
|
||||
try {
|
||||
matcher.group(1)!!.toLong()
|
||||
} catch (e: NumberFormatException) {
|
||||
-1
|
||||
},
|
||||
matcher.group(2)
|
||||
), state
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun <RC, S> createInlineCodeRule(context: Context, backgroundColor: Int): Rule<RC, Node<RC>, S> {
|
||||
return CodeRules.createInlineCodeRule(
|
||||
{ listOf(TextAppearanceSpan(context, R.style.Code_TextAppearance)) },
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ import chat.revolt.internals.markdown.CustomEmoteRule
|
|||
import chat.revolt.internals.markdown.MarkdownContext
|
||||
import chat.revolt.internals.markdown.MarkdownParser
|
||||
import chat.revolt.internals.markdown.MarkdownState
|
||||
import chat.revolt.internals.markdown.TimestampRule
|
||||
import chat.revolt.internals.markdown.UserMentionRule
|
||||
import chat.revolt.internals.markdown.createCodeRule
|
||||
import chat.revolt.internals.markdown.createInlineCodeRule
|
||||
|
|
@ -256,6 +257,7 @@ fun ChannelScreen(
|
|||
UserMentionRule(),
|
||||
ChannelMentionRule(),
|
||||
CustomEmoteRule(),
|
||||
TimestampRule(),
|
||||
)
|
||||
.addRules(
|
||||
createCodeRule(context, codeBlockColor.toArgb()),
|
||||
|
|
|
|||
Loading…
Reference in New Issue