feat: support webhooks
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
197c6db56a
commit
7e2eccf387
|
|
@ -20,6 +20,7 @@ data class Message(
|
||||||
val mentions: List<String>? = null,
|
val mentions: List<String>? = null,
|
||||||
val masquerade: Masquerade? = null,
|
val masquerade: Masquerade? = null,
|
||||||
val system: SystemInfo? = null,
|
val system: SystemInfo? = null,
|
||||||
|
val webhook: WebHook? = null,
|
||||||
val type: String? = null, // this is _only_ used for websocket events!
|
val type: String? = null, // this is _only_ used for websocket events!
|
||||||
val tail: Boolean? = null // this is used to determine if the message is the last in a message group
|
val tail: Boolean? = null // this is used to determine if the message is the last in a message group
|
||||||
) {
|
) {
|
||||||
|
|
@ -110,3 +111,9 @@ data class SystemInfo(
|
||||||
val to: String? = null,
|
val to: String? = null,
|
||||||
val content: String? = null
|
val content: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class WebHook(
|
||||||
|
val avatar: String? = null,
|
||||||
|
val name: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -14,6 +14,7 @@ sealed class Action {
|
||||||
data class ReportUser(val userId: String) : Action()
|
data class ReportUser(val userId: String) : Action()
|
||||||
data class ReportMessage(val messageId: String) : Action()
|
data class ReportMessage(val messageId: String) : Action()
|
||||||
data class OpenVoiceChannelOverlay(val channelId: String) : Action()
|
data class OpenVoiceChannelOverlay(val channelId: String) : Action()
|
||||||
|
data object OpenWebhookSheet : Action()
|
||||||
}
|
}
|
||||||
|
|
||||||
val ActionChannel = Channel<Action>(
|
val ActionChannel = Channel<Action>(
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ enum class InlineBadge {
|
||||||
Bot,
|
Bot,
|
||||||
Bridge,
|
Bridge,
|
||||||
PlatformModeration,
|
PlatformModeration,
|
||||||
TeamMember
|
TeamMember,
|
||||||
|
Webhook
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -50,6 +51,13 @@ fun InlineBadge(
|
||||||
tint = colour,
|
tint = colour,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
|
|
||||||
|
InlineBadge.Webhook -> Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_hook_24dp),
|
||||||
|
contentDescription = stringResource(id = R.string.badge_webhook_alt),
|
||||||
|
tint = colour,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,6 +68,7 @@ fun InlineBadges(
|
||||||
bridge: Boolean = false,
|
bridge: Boolean = false,
|
||||||
platformModeration: Boolean = false,
|
platformModeration: Boolean = false,
|
||||||
teamMember: Boolean = false,
|
teamMember: Boolean = false,
|
||||||
|
webhook: Boolean = false,
|
||||||
colour: Color = Color.Unspecified,
|
colour: Color = Color.Unspecified,
|
||||||
precedingIfAny: @Composable () -> Unit = {},
|
precedingIfAny: @Composable () -> Unit = {},
|
||||||
followingIfAny: @Composable () -> Unit = {}
|
followingIfAny: @Composable () -> Unit = {}
|
||||||
|
|
@ -99,6 +108,13 @@ fun InlineBadges(
|
||||||
colour = colour
|
colour = colour
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (webhook) {
|
||||||
|
InlineBadge(
|
||||||
|
badge = InlineBadge.Webhook,
|
||||||
|
modifier = modifier,
|
||||||
|
colour = colour
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasBadges) {
|
if (hasBadges) {
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,8 @@ fun Message(
|
||||||
canReply: Boolean = false,
|
canReply: Boolean = false,
|
||||||
onReply: () -> Unit = {},
|
onReply: () -> Unit = {},
|
||||||
onAddReaction: () -> Unit = {},
|
onAddReaction: () -> Unit = {},
|
||||||
|
fromWebhook: Boolean = false,
|
||||||
|
webhookName: String? = null
|
||||||
) {
|
) {
|
||||||
val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator()
|
val author = RevoltAPI.userCache[message.author] ?: return CircularProgressIndicator()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -304,7 +306,7 @@ fun Message(
|
||||||
if (message.tail == false) {
|
if (message.tail == false) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = authorName(message),
|
text = webhookName ?: authorName(message),
|
||||||
style = LocalTextStyle.current.copy(
|
style = LocalTextStyle.current.copy(
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
brush = authorColour(message)
|
brush = authorColour(message)
|
||||||
|
|
@ -328,6 +330,7 @@ fun Message(
|
||||||
bridge = message.masquerade != null && author.bot != null,
|
bridge = message.masquerade != null && author.bot != null,
|
||||||
platformModeration = author.id == SpecialUsers.PLATFORM_MODERATION_USER,
|
platformModeration = author.id == SpecialUsers.PLATFORM_MODERATION_USER,
|
||||||
teamMember = author.id in SpecialUsers.TEAM_MEMBER_FLAIRS.keys,
|
teamMember = author.id in SpecialUsers.TEAM_MEMBER_FLAIRS.keys,
|
||||||
|
webhook = fromWebhook,
|
||||||
colour = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.5f),
|
colour = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.5f),
|
||||||
modifier = Modifier.size(16.dp),
|
modifier = Modifier.size(16.dp),
|
||||||
precedingIfAny = {
|
precedingIfAny = {
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ import chat.revolt.sheets.ReactionInfoSheet
|
||||||
import chat.revolt.sheets.ServerContextSheet
|
import chat.revolt.sheets.ServerContextSheet
|
||||||
import chat.revolt.sheets.StatusSheet
|
import chat.revolt.sheets.StatusSheet
|
||||||
import chat.revolt.sheets.UserInfoSheet
|
import chat.revolt.sheets.UserInfoSheet
|
||||||
|
import chat.revolt.sheets.WebHookUserSheet
|
||||||
import com.airbnb.lottie.RenderMode
|
import com.airbnb.lottie.RenderMode
|
||||||
import com.airbnb.lottie.compose.LottieAnimation
|
import com.airbnb.lottie.compose.LottieAnimation
|
||||||
import com.airbnb.lottie.compose.LottieCompositionSpec
|
import com.airbnb.lottie.compose.LottieCompositionSpec
|
||||||
|
|
@ -229,6 +230,8 @@ fun ChatRouterScreen(
|
||||||
var userContextSheetTarget by remember { mutableStateOf("") }
|
var userContextSheetTarget by remember { mutableStateOf("") }
|
||||||
var userContextSheetServer by remember { mutableStateOf<String?>(null) }
|
var userContextSheetServer by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
var showWebhookInfoSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var showChannelUnavailableAlert by remember { mutableStateOf(false) }
|
var showChannelUnavailableAlert by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var showLinkInfoSheet by remember { mutableStateOf(false) }
|
var showLinkInfoSheet by remember { mutableStateOf(false) }
|
||||||
|
|
@ -389,6 +392,10 @@ fun ChatRouterScreen(
|
||||||
voiceChannelOverlayChannelId = action.channelId
|
voiceChannelOverlayChannelId = action.channelId
|
||||||
voiceChannelOverlay = true
|
voiceChannelOverlay = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is Action.OpenWebhookSheet -> {
|
||||||
|
showWebhookInfoSheet = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -554,6 +561,19 @@ fun ChatRouterScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showWebhookInfoSheet) {
|
||||||
|
val webhookInfoSheetState = rememberModalBottomSheetState()
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
sheetState = webhookInfoSheetState,
|
||||||
|
onDismissRequest = {
|
||||||
|
showWebhookInfoSheet = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
WebHookUserSheet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (showReportUser) {
|
if (showReportUser) {
|
||||||
ReportUserDialog(
|
ReportUserDialog(
|
||||||
onDismiss = { showReportUser = false },
|
onDismiss = { showReportUser = false },
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,6 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
sealed class ChannelScreenItem {
|
sealed class ChannelScreenItem {
|
||||||
data class RegularMessage(val message: Message) : ChannelScreenItem()
|
data class RegularMessage(val message: Message) : ChannelScreenItem()
|
||||||
|
|
@ -603,14 +602,20 @@ fun ChannelScreen(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onAvatarClick = {
|
onAvatarClick = {
|
||||||
item.message.author?.let { author ->
|
if (item.message.webhook != null) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
ActionChannel.send(
|
ActionChannel.send(Action.OpenWebhookSheet)
|
||||||
Action.OpenUserSheet(
|
}
|
||||||
author,
|
} else {
|
||||||
viewModel.channel?.server
|
item.message.author?.let { author ->
|
||||||
|
scope.launch {
|
||||||
|
ActionChannel.send(
|
||||||
|
Action.OpenUserSheet(
|
||||||
|
author,
|
||||||
|
viewModel.channel?.server
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -631,7 +636,9 @@ fun ChannelScreen(
|
||||||
reactSheetTarget = messageId
|
reactSheetTarget = messageId
|
||||||
reactSheetShown = true
|
reactSheetShown = true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
fromWebhook = item.message.webhook != null,
|
||||||
|
webhookName = item.message.webhook?.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package chat.revolt.sheets
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import chat.revolt.R
|
||||||
|
import chat.revolt.components.generic.NonIdealState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WebHookUserSheet(modifier: Modifier = Modifier) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
NonIdealState(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_hook_24dp),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.user_info_sheet_webhook)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
description = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.user_info_sheet_webhook_description),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Column(Modifier.padding(horizontal = 16.dp)) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.user_info_sheet_webhook_description_2),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(32.dp))
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
Toast(context).apply {
|
||||||
|
setText(context.getString(R.string.comingsoon_toast))
|
||||||
|
duration = Toast.LENGTH_SHORT
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.user_info_sheet_webhook_learn_more))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(48.dp))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M18,6C18,7.82 16.76,9.41 15,9.86V17A5,5 0 0,1 10,22A5,5 0 0,1 5,17V12L10,17H7A3,3 0 0,0 10,20A3,3 0 0,0 13,17V9.86C11.23,9.4 10,7.8 10,5.97C10,3.76 11.8,2 14,2C16.22,2 18,3.79 18,6M14,8A2,2 0 0,0 16,6A2,2 0 0,0 14,4A2,2 0 0,0 12,6A2,2 0 0,0 14,8Z" />
|
||||||
|
</vector>
|
||||||
|
|
@ -125,6 +125,7 @@
|
||||||
<string name="badge_masquerade_alt">From linked channel</string>
|
<string name="badge_masquerade_alt">From linked channel</string>
|
||||||
<string name="badge_platform_moderation_alt">Platform moderation</string>
|
<string name="badge_platform_moderation_alt">Platform moderation</string>
|
||||||
<string name="badge_team_member_alt">Revolt team member</string>
|
<string name="badge_team_member_alt">Revolt team member</string>
|
||||||
|
<string name="badge_webhook_alt">Webhook</string>
|
||||||
|
|
||||||
<string name="unknown">Unknown</string>
|
<string name="unknown">Unknown</string>
|
||||||
|
|
||||||
|
|
@ -352,6 +353,10 @@
|
||||||
<string name="user_info_sheet_open">Open user info</string>
|
<string name="user_info_sheet_open">Open user info</string>
|
||||||
<string name="user_info_sheet_user_not_found">Can\'t resolve this user</string>
|
<string name="user_info_sheet_user_not_found">Can\'t resolve this user</string>
|
||||||
<string name="user_info_sheet_user_not_found_description">This user may have been deleted or you may not have permission to view them.</string>
|
<string name="user_info_sheet_user_not_found_description">This user may have been deleted or you may not have permission to view them.</string>
|
||||||
|
<string name="user_info_sheet_webhook">Is it a bird, is it a plane?</string>
|
||||||
|
<string name="user_info_sheet_webhook_description">No, it\'s a webhook!</string>
|
||||||
|
<string name="user_info_sheet_webhook_description_2">Webhooks are automated broadcasts from other services on the internet. They can\'t chat back, but you can still see what they have to say.</string>
|
||||||
|
<string name="user_info_sheet_webhook_learn_more">Learn more</string>
|
||||||
<string name="user_info_sheet_category_bio">Bio</string>
|
<string name="user_info_sheet_category_bio">Bio</string>
|
||||||
<string name="user_info_sheet_bio_empty">This user hasn\'t set a bio yet.</string>
|
<string name="user_info_sheet_bio_empty">This user hasn\'t set a bio yet.</string>
|
||||||
<string name="user_info_sheet_bio_not_found">This user\'s bio could not be fetched. Please verify you share a server or are friends.</string>
|
<string name="user_info_sheet_bio_not_found">This user\'s bio could not be fetched. Please verify you share a server or are friends.</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue