feat: intercept invite links in md, mentions clickable
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
a2381fa005
commit
ba6edec5a5
|
|
@ -46,6 +46,7 @@ const val REVOLT_MARKETING = "https://revolt.chat"
|
|||
const val REVOLT_FILES = "https://autumn.revolt.chat"
|
||||
const val REVOLT_JANUARY = "https://jan.revolt.chat"
|
||||
const val REVOLT_APP = "https://app.revolt.chat"
|
||||
const val REVOLT_INVITES = "https://rvlt.gg"
|
||||
const val REVOLT_WEBSOCKET = "wss://ws.revolt.chat"
|
||||
|
||||
fun buildUserAgent(accessMethod: String = "Ktor"): String {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package chat.revolt.callbacks
|
||||
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
|
||||
sealed class Action {
|
||||
data class OpenUserSheet(val userId: String, val serverId: String?) : Action()
|
||||
}
|
||||
|
||||
val ActionChannel = Channel<Action>(
|
||||
Channel.BUFFERED
|
||||
)
|
||||
|
|
@ -1,13 +1,59 @@
|
|||
package chat.revolt.internals.markdown
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.text.TextPaint
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.View
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import chat.revolt.activities.InviteActivity
|
||||
import chat.revolt.api.REVOLT_APP
|
||||
import chat.revolt.api.REVOLT_INVITES
|
||||
import chat.revolt.callbacks.Action
|
||||
import chat.revolt.callbacks.ActionChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class LinkSpan(private val url: String) : ClickableSpan() {
|
||||
class LinkSpan(private val url: String, private val drawBackground: Boolean = false) :
|
||||
ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
val uri = Uri.parse(url)
|
||||
|
||||
// Intercept invite links
|
||||
if (uri.host == Uri.parse(REVOLT_INVITES).host!! ||
|
||||
(uri.host?.endsWith(Uri.parse(REVOLT_APP).host!!) == true && uri.path?.startsWith(
|
||||
"/invite"
|
||||
) == true)
|
||||
) {
|
||||
val intent = Intent(
|
||||
widget.context,
|
||||
InviteActivity::class.java
|
||||
).setAction(Intent.ACTION_VIEW)
|
||||
|
||||
intent.data = uri
|
||||
widget.context.startActivity(intent)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (url.startsWith("revolt-android://link-action")) {
|
||||
// parse action
|
||||
val action = uri.pathSegments[0]
|
||||
|
||||
when (action) {
|
||||
"user" -> {
|
||||
val userId = uri.getQueryParameter("user")
|
||||
val serverId = uri.getQueryParameter("server")
|
||||
|
||||
runBlocking(Dispatchers.IO) {
|
||||
ActionChannel.send(Action.OpenUserSheet(userId!!, serverId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val customTab = CustomTabsIntent.Builder()
|
||||
.setShowTitle(true)
|
||||
.build()
|
||||
|
|
@ -18,5 +64,8 @@ class LinkSpan(private val url: String) : ClickableSpan() {
|
|||
override fun updateDrawState(ds: TextPaint) {
|
||||
ds.color = ds.linkColor
|
||||
ds.isUnderlineText = false
|
||||
if (drawBackground) {
|
||||
ds.bgColor = ds.linkColor and 0x33ffffff // 20% alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,10 +14,23 @@ import java.util.Locale
|
|||
|
||||
class UserMentionNode(private val userId: String) : Node<MarkdownContext>() {
|
||||
override fun render(builder: SpannableStringBuilder, renderContext: MarkdownContext) {
|
||||
builder.append(
|
||||
renderContext.memberMap[userId]?.let { "@$it" }
|
||||
?: renderContext.userMap[userId]?.let { "@${it.username}" }
|
||||
?: "<@${userId}>"
|
||||
val content = renderContext.memberMap[userId]?.let { "@$it" }
|
||||
?: renderContext.userMap[userId]?.let { "@${it.username}" }
|
||||
?: "<@${userId}>"
|
||||
|
||||
builder.append(content)
|
||||
builder.setSpan(
|
||||
LinkSpan(
|
||||
"revolt-android://link-action/user?user=$userId${
|
||||
renderContext.serverId?.let {
|
||||
"&server=$it"
|
||||
}.orEmpty()
|
||||
}",
|
||||
drawBackground = true
|
||||
),
|
||||
builder.length - content.length,
|
||||
builder.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ import chat.revolt.api.schemas.ChannelType
|
|||
import chat.revolt.api.schemas.User
|
||||
import chat.revolt.api.settings.FeatureFlag
|
||||
import chat.revolt.api.settings.SyncedSettings
|
||||
import chat.revolt.callbacks.Action
|
||||
import chat.revolt.callbacks.ActionChannel
|
||||
import chat.revolt.components.chat.DisconnectedNotice
|
||||
import chat.revolt.components.generic.GroupIcon
|
||||
import chat.revolt.components.generic.UserAvatar
|
||||
|
|
@ -348,6 +350,20 @@ fun ChatRouterScreen(
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
ActionChannel.receive().let { action ->
|
||||
when (action) {
|
||||
is Action.OpenUserSheet -> {
|
||||
userContextSheetTarget = action.userId
|
||||
userContextSheetServer = action.serverId
|
||||
showUserContextSheet = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!viewModel.latestChangelogRead) {
|
||||
val changelogSheetState = rememberModalBottomSheetState()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue