feat: styling for timestamps
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
a3c5e43c5d
commit
800aa68083
|
|
@ -58,7 +58,7 @@ fun UIMarkdown(
|
|||
.addRules(
|
||||
SimpleMarkdownRules.createEscapeRule()
|
||||
)
|
||||
.addRevoltRules()
|
||||
.addRevoltRules(context)
|
||||
.addRules(
|
||||
createCodeRule(context, codeBlockColor.toArgb()),
|
||||
createInlineCodeRule(context, codeBlockColor.toArgb()),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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>"
|
||||
}
|
||||
}
|
||||
|
|
@ -298,7 +298,7 @@ fun ChannelScreen(
|
|||
.addRules(
|
||||
SimpleMarkdownRules.createEscapeRule(),
|
||||
)
|
||||
.addRevoltRules()
|
||||
.addRevoltRules(context)
|
||||
.addRules(
|
||||
createCodeRule(context, codeBlockColor.toArgb()),
|
||||
createInlineCodeRule(context, codeBlockColor.toArgb()),
|
||||
|
|
|
|||
Loading…
Reference in New Issue