feat: styling for timestamps

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2023-09-17 17:55:52 +02:00
parent a3c5e43c5d
commit 800aa68083
5 changed files with 87 additions and 86 deletions

View File

@ -58,7 +58,7 @@ fun UIMarkdown(
.addRules(
SimpleMarkdownRules.createEscapeRule()
)
.addRevoltRules()
.addRevoltRules(context)
.addRules(
createCodeRule(context, codeBlockColor.toArgb()),
createInlineCodeRule(context, codeBlockColor.toArgb()),

View File

@ -2,15 +2,7 @@ package chat.revolt.internals.markdown
import android.text.SpannableStringBuilder
import android.text.Spanned
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) {
@ -62,70 +54,6 @@ 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>")
}
}
}
class LinkNode(val content: String, val url: String = content) : Node<MarkdownContext>() {
override fun render(builder: SpannableStringBuilder, renderContext: MarkdownContext) {
builder.append(content)

View File

@ -47,21 +47,24 @@ class CustomEmoteRule<S> :
}
}
class TimestampRule<S> :
Rule<MarkdownContext, TimestampNode, S>(Pattern.compile("^<t:([0-9]+?)(:[tTDfFR])?>")) {
class TimestampRule<S>(private val context: Context) :
Rule<MarkdownContext, Node<MarkdownContext>, S>(Pattern.compile("^<t:([0-9]+?)(:[tTDfFR])?>")) {
override fun parse(
matcher: Matcher,
parser: Parser<MarkdownContext, in TimestampNode, S>,
parser: Parser<MarkdownContext, in Node<MarkdownContext>, S>,
state: S
): ParseSpec<MarkdownContext, S> {
return ParseSpec.createTerminal(
TimestampNode(
try {
matcher.group(1)!!.toLong()
} catch (e: NumberFormatException) {
-1
},
matcher.group(2)
StyleNode.wrapText(
resolveTimestamp(
try {
matcher.group(1)!!.toLong()
} catch (e: NumberFormatException) {
-1
},
matcher.group(2)
),
listOf(TextAppearanceSpan(context, R.style.Code_TextAppearance))
), state
)
}
@ -193,12 +196,12 @@ fun <RC> createCodeRule(
}
}
fun MarkdownParser.addRevoltRules(): MarkdownParser {
fun MarkdownParser.addRevoltRules(context: Context): MarkdownParser {
return addRules(
UserMentionRule(),
ChannelMentionRule(),
CustomEmoteRule(),
TimestampRule(),
TimestampRule(context),
NamedLinkRule(),
LinkRule(),
)

View File

@ -0,0 +1,70 @@
package chat.revolt.internals.markdown
import android.text.format.DateUtils
import android.util.Log
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
fun resolveTimestamp(timestamp: Long, modifier: String? = null): String {
val normalisedModifier = modifier.orEmpty().removePrefix(":")
val instant = Instant.fromEpochSeconds(timestamp)
val javaInstant = instant.toJavaInstant()
try {
if (timestamp < 0) {
return "<invalid timestamp>"
}
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, regex already checks for this
else -> timestamp.toString()
}
return outString.toString()
} catch (e: Exception) {
Log.e("Timestamp", "Failed to parse timestamp", e)
return "<invalid timestamp>"
}
}

View File

@ -298,7 +298,7 @@ fun ChannelScreen(
.addRules(
SimpleMarkdownRules.createEscapeRule(),
)
.addRevoltRules()
.addRevoltRules(context)
.addRules(
createCodeRule(context, codeBlockColor.toArgb()),
createInlineCodeRule(context, codeBlockColor.toArgb()),