feat: link and bot embeds
This commit is contained in:
parent
40a9cdf344
commit
219d252912
|
|
@ -112,8 +112,8 @@ dependencies {
|
|||
implementation 'androidx.compose.material:material'
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation "androidx.compose.ui:ui-tooling-preview"
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.0'
|
||||
implementation 'androidx.activity:activity-compose:1.6.1'
|
||||
|
||||
// Accompanist - Jetpack Compose Extensions
|
||||
|
|
@ -144,7 +144,7 @@ dependencies {
|
|||
|
||||
// Glide - Image Loading
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
implementation "com.github.bumptech.glide:compose:1.0.0-alpha.1"
|
||||
implementation "com.github.bumptech.glide:compose:1.0.0-alpha.4-SNAPSHOT"
|
||||
|
||||
// AboutLibraries - automated OSS library attribution
|
||||
implementation "com.mikepenz:aboutlibraries-compose:$aboutlibraries_version"
|
||||
|
|
|
|||
|
|
@ -10,11 +10,15 @@ object WebCompat {
|
|||
@Composable
|
||||
fun parseColour(colour: String): Color {
|
||||
if (colour.startsWith("var(")) {
|
||||
Log.d(
|
||||
"WebCompat",
|
||||
"Parsing colour $colour. ${colour.substringAfter("var(").substringBefore(")")}"
|
||||
)
|
||||
return when (colour.substringAfter("var(").substringBefore(")")) {
|
||||
"accent" -> MaterialTheme.colorScheme.primary
|
||||
"foreground" -> MaterialTheme.colorScheme.onBackground
|
||||
"background" -> MaterialTheme.colorScheme.background
|
||||
"error" -> MaterialTheme.colorScheme.error
|
||||
"--accent" -> MaterialTheme.colorScheme.primary
|
||||
"--foreground" -> MaterialTheme.colorScheme.onBackground
|
||||
"--background" -> MaterialTheme.colorScheme.background
|
||||
"--error" -> MaterialTheme.colorScheme.error
|
||||
else -> LocalContentColor.current
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,11 @@ suspend fun fetchSelf(): User {
|
|||
|
||||
val user = RevoltJson.decodeFromString(User.serializer(), response)
|
||||
|
||||
RevoltAPI.userCache[user.id!!] = user
|
||||
if (user.id == null) {
|
||||
throw Error("Self user ID is null")
|
||||
}
|
||||
|
||||
RevoltAPI.userCache[user.id] = user
|
||||
RevoltAPI.selfId = user.id
|
||||
|
||||
return user
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
package chat.revolt.components.chat
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.api.asJanuaryProxyUrl
|
||||
import chat.revolt.api.internals.WebCompat
|
||||
import chat.revolt.api.schemas.Embed
|
||||
import chat.revolt.components.generic.RemoteImage
|
||||
import chat.revolt.components.generic.UIMarkdown
|
||||
import chat.revolt.api.schemas.Embed as EmbedSchema
|
||||
|
||||
@Composable
|
||||
fun RegularEmbed(
|
||||
embed: EmbedSchema,
|
||||
modifier: Modifier = Modifier,
|
||||
onLinkClick: (String) -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
||||
.fillMaxWidth()
|
||||
.height(IntrinsicSize.Min)
|
||||
) {
|
||||
// Stripe at the left side of the embed
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(4.dp)
|
||||
.fillMaxHeight()
|
||||
.background(
|
||||
embed.colour?.let { WebCompat.parseColour(it) }
|
||||
?: MaterialTheme.colorScheme.primary
|
||||
)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Column {
|
||||
// Title row (icon + title)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.then(
|
||||
if (embed.originalURL != null)
|
||||
Modifier
|
||||
.clickable {
|
||||
onLinkClick(embed.originalURL)
|
||||
}
|
||||
else Modifier
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
embed.iconURL?.let {
|
||||
if (it.endsWith(".svg")) return@let
|
||||
|
||||
RemoteImage(
|
||||
url = asJanuaryProxyUrl(it),
|
||||
width = 48,
|
||||
height = 48,
|
||||
contentScale = ContentScale.Crop,
|
||||
description = null // decorative
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
embed.title?.let {
|
||||
Text(
|
||||
text = it,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Description
|
||||
embed.description?.let {
|
||||
UIMarkdown(
|
||||
text = it,
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
// Image
|
||||
embed.image?.let {
|
||||
if (it.url == null || it.url.endsWith(".svg")) return@let
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
RemoteImage(
|
||||
url = asJanuaryProxyUrl(it.url),
|
||||
width = (it.width ?: 48).toInt(),
|
||||
height = (it.height ?: 48).toInt(),
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.then(
|
||||
if (embed.originalURL != null) Modifier.clickable {
|
||||
onLinkClick(embed.originalURL)
|
||||
} else Modifier
|
||||
)
|
||||
.aspectRatio(
|
||||
(it.width?.toFloat() ?: 0f) / (it.height?.toFloat() ?: 0f)
|
||||
),
|
||||
contentScale = ContentScale.Crop,
|
||||
description = null // decorative
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Embed(
|
||||
embed: Embed,
|
||||
modifier: Modifier = Modifier,
|
||||
onLinkClick: (String) -> Unit
|
||||
) {
|
||||
Column {
|
||||
when (embed.type) {
|
||||
else -> RegularEmbed(embed = embed, modifier = modifier, onLinkClick = onLinkClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,14 +31,16 @@ import chat.revolt.components.generic.UserAvatar
|
|||
import chat.revolt.components.generic.UserAvatarWidthPlaceholder
|
||||
import chat.revolt.api.schemas.Message as MessageSchema
|
||||
|
||||
fun viewAttachmentInBrowser(ctx: android.content.Context, attachment: AutumnResource) {
|
||||
fun viewUrlInBrowser(ctx: android.content.Context, url: String) {
|
||||
val customTab = CustomTabsIntent
|
||||
.Builder()
|
||||
.build()
|
||||
customTab.launchUrl(
|
||||
ctx,
|
||||
Uri.parse("$REVOLT_FILES/attachments/${attachment.id}/${attachment.filename}")
|
||||
)
|
||||
customTab.launchUrl(ctx, Uri.parse(url))
|
||||
}
|
||||
|
||||
fun viewAttachmentInBrowser(ctx: android.content.Context, attachment: AutumnResource) {
|
||||
val url = REVOLT_FILES + attachment.id + "/" + attachment.filename
|
||||
viewUrlInBrowser(ctx, url)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -159,10 +161,31 @@ fun Message(
|
|||
|
||||
message.attachments?.let {
|
||||
message.attachments.forEach { attachment ->
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
MessageAttachment(attachment) {
|
||||
viewAttachmentInBrowser(context, attachment)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
}
|
||||
}
|
||||
|
||||
message.embeds?.let {
|
||||
message.embeds.forEach { embed ->
|
||||
val embedIsEmpty =
|
||||
embed.title == null && embed.description == null && embed.iconURL == null && embed.image == null
|
||||
|
||||
if (embedIsEmpty) {
|
||||
// if we do not emit anything, compose will cause an internal error.
|
||||
// FIXME if you are doing fixme's anyways then check if this is still an issue
|
||||
Box {}
|
||||
return@forEach
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Embed(embed = embed, onLinkClick = {
|
||||
viewUrlInBrowser(context, it)
|
||||
})
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.text.SpannableStringBuilder
|
|||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -42,8 +43,8 @@ import com.discord.simpleast.core.simple.SimpleRenderer
|
|||
@Composable
|
||||
fun UIMarkdown(
|
||||
text: String,
|
||||
fontSize: TextUnit,
|
||||
modifier: Modifier = Modifier,
|
||||
fontSize: TextUnit = LocalTextStyle.current.fontSize,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
|
@ -96,8 +97,6 @@ fun UIMarkdown(
|
|||
setTextColor(foregroundColor.toArgb())
|
||||
setMaxLines(maxLines)
|
||||
setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, fontSize.value)
|
||||
|
||||
setText(spannableStringBuilder.value)
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import com.bumptech.glide.integration.compose.GlideImage
|
|||
@Composable
|
||||
fun RemoteImage(
|
||||
url: String,
|
||||
description: String,
|
||||
description: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
contentScale: ContentScale = ContentScale.Crop,
|
||||
width: Int = 0,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ dependencyResolutionManagement {
|
|||
google()
|
||||
mavenCentral()
|
||||
maven { url "https://jitpack.io" }
|
||||
maven {
|
||||
name 'snapshot'
|
||||
url 'https://oss.sonatype.org/content/repositories/snapshots'
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "Revolt"
|
||||
|
|
|
|||
Loading…
Reference in New Issue