diff --git a/app/src/main/java/chat/revolt/api/schemas/Messages.kt b/app/src/main/java/chat/revolt/api/schemas/Messages.kt
index 21a08f70..f68f122c 100644
--- a/app/src/main/java/chat/revolt/api/schemas/Messages.kt
+++ b/app/src/main/java/chat/revolt/api/schemas/Messages.kt
@@ -95,5 +95,10 @@ data class Masquerade(
@Serializable
data class SystemInfo(
val type: String? = null,
- val id: String? = null
+ val id: String? = null,
+ val name: String? = null,
+ val by: String? = null,
+ val from: String? = null,
+ val to: String? = null,
+ val content: String? = null,
)
\ No newline at end of file
diff --git a/app/src/main/java/chat/revolt/components/chat/SystemMessage.kt b/app/src/main/java/chat/revolt/components/chat/SystemMessage.kt
new file mode 100644
index 00000000..c7e1528f
--- /dev/null
+++ b/app/src/main/java/chat/revolt/components/chat/SystemMessage.kt
@@ -0,0 +1,245 @@
+package chat.revolt.components.chat
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Info
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import chat.revolt.R
+import chat.revolt.api.schemas.Message
+
+enum class SystemMessageType(val type: String) {
+ CHANNEL_OWNERSHIP_CHANGED("channel_ownership_changed"),
+ CHANNEL_ICON_CHANGED("channel_icon_changed"),
+ CHANNEL_DESCRIPTION_CHANGED("channel_description_changed"),
+ CHANNEL_RENAMED("channel_renamed"),
+ USER_REMOVE("user_remove"),
+ USER_ADDED("user_added"),
+ USER_BANNED("user_banned"),
+ USER_KICKED("user_kicked"),
+ USER_LEFT("user_left"),
+ USER_JOINED("user_joined"),
+ TEXT("text"),
+}
+
+@Composable
+fun SystemMessage(
+ message: Message
+) {
+ if (message.system == null) return
+
+ val systemMessageType =
+ SystemMessageType.values().firstOrNull { it.type == message.system.type }
+
+ if (systemMessageType == null) {
+ Text(text = message.system.toString())
+ return
+ }
+
+ CompositionLocalProvider(
+ LocalContentColor provides LocalContentColor.current.copy(alpha = 0.7f),
+ LocalTextStyle provides LocalTextStyle.current.copy(
+ fontWeight = FontWeight.Light
+ )
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp)
+ ) {
+ SystemMessageIconWithBackground(type = systemMessageType)
+
+ Spacer(modifier = Modifier.width(10.dp))
+
+ when (systemMessageType) {
+ SystemMessageType.CHANNEL_OWNERSHIP_CHANGED -> {
+ Text(text = "Channel ownership changed from ${message.system.from} to ${message.system.to}")
+ }
+
+ SystemMessageType.CHANNEL_ICON_CHANGED -> {
+ Text(text = "Channel icon changed by ${message.system.by}")
+ }
+
+ SystemMessageType.CHANNEL_DESCRIPTION_CHANGED -> {
+ Text(text = "Channel description changed by ${message.system.by}")
+ }
+
+ SystemMessageType.CHANNEL_RENAMED -> {
+ Text(text = "Channel renamed to ${message.system.name} by ${message.system.by}")
+ }
+
+ SystemMessageType.USER_REMOVE -> {
+ Text(text = "User ${message.system.id} removed by ${message.system.by}")
+ }
+
+ SystemMessageType.USER_ADDED -> {
+ Text(text = "User ${message.system.id} added by ${message.system.by}")
+ }
+
+ SystemMessageType.USER_BANNED -> {
+ Text(text = "User ${message.system.id} banned")
+ }
+
+ SystemMessageType.USER_KICKED -> {
+ Text(text = "User ${message.system.id} kicked")
+ }
+
+ SystemMessageType.USER_LEFT -> {
+ Text(text = "User ${message.system.id} left")
+ }
+
+ SystemMessageType.USER_JOINED -> {
+ Text(text = "User ${message.system.id} joined")
+ }
+
+ SystemMessageType.TEXT -> {
+ message.system.content?.let { Text(text = it) }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun SystemMessageIcon(
+ type: SystemMessageType,
+ modifier: Modifier = Modifier,
+ size: Dp = 24.dp
+) {
+ when (type) {
+ SystemMessageType.CHANNEL_OWNERSHIP_CHANGED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_key_arrow_right_24dp),
+ contentDescription = stringResource(R.string.system_message_ownership_changed_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.CHANNEL_ICON_CHANGED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_image_multiple_24dp),
+ contentDescription = stringResource(R.string.system_message_channel_icon_changed_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.CHANNEL_DESCRIPTION_CHANGED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_text_box_multiple_24dp),
+ contentDescription = stringResource(R.string.system_message_channel_description_changed_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.CHANNEL_RENAMED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_cursor_text_24dp),
+ contentDescription = stringResource(R.string.system_message_channel_renamed_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.USER_REMOVE -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_account_cancel_24dp),
+ contentDescription = stringResource(R.string.system_message_user_removed_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.USER_ADDED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_account_plus_24dp),
+ contentDescription = stringResource(R.string.system_message_user_added_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.USER_BANNED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_gavel_24dp),
+ contentDescription = stringResource(R.string.system_message_user_banned_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.USER_KICKED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_shield_24dp),
+ contentDescription = stringResource(R.string.system_message_user_kicked_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.USER_LEFT -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_account_arrow_left_24dp),
+ contentDescription = stringResource(R.string.system_message_user_left_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.USER_JOINED -> {
+ Icon(
+ painter = painterResource(R.drawable.ic_account_arrow_right_24dp),
+ contentDescription = stringResource(R.string.system_message_user_joined_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+
+ SystemMessageType.TEXT -> {
+ Icon(
+ imageVector = Icons.Default.Info,
+ contentDescription = stringResource(R.string.system_message_text_alt),
+ tint = LocalContentColor.current,
+ modifier = modifier.size(size)
+ )
+ }
+ }
+}
+
+@Composable
+fun SystemMessageIconWithBackground(
+ type: SystemMessageType,
+ modifier: Modifier = Modifier,
+ size: Dp = 40.dp
+) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = modifier
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.primary.copy(alpha = 0.3f))
+ .size(size)
+ ) {
+ SystemMessageIcon(type = type)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt
index d1826df9..95592280 100644
--- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt
+++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt
@@ -60,6 +60,7 @@ import chat.revolt.api.internals.ChannelUtils
import chat.revolt.api.routes.microservices.autumn.FileArgs
import chat.revolt.components.chat.Message
import chat.revolt.components.chat.MessageField
+import chat.revolt.components.chat.SystemMessage
import chat.revolt.components.screens.chat.AttachmentManager
import chat.revolt.components.screens.chat.ChannelHeader
import chat.revolt.components.screens.chat.ReplyManager
@@ -244,63 +245,66 @@ fun ChannelScreen(
items = viewModel.renderableMessages,
key = { it.id!! }
) { message ->
- Message(
- message,
- parse = {
- val parser = MarkdownParser()
- .addRules(
- SimpleMarkdownRules.createEscapeRule(),
- UserMentionRule(),
- ChannelMentionRule(),
- CustomEmoteRule(),
- )
- .addRules(
- createCodeRule(context, codeBlockColor.toArgb()),
- createInlineCodeRule(context, codeBlockColor.toArgb()),
- )
- .addRules(
- SimpleMarkdownRules.createSimpleMarkdownRules(
- includeEscapeRule = false
+ when {
+ message.system != null -> SystemMessage(message)
+ else -> Message(
+ message,
+ parse = {
+ val parser = MarkdownParser()
+ .addRules(
+ SimpleMarkdownRules.createEscapeRule(),
+ UserMentionRule(),
+ ChannelMentionRule(),
+ CustomEmoteRule(),
+ )
+ .addRules(
+ createCodeRule(context, codeBlockColor.toArgb()),
+ createInlineCodeRule(context, codeBlockColor.toArgb()),
+ )
+ .addRules(
+ SimpleMarkdownRules.createSimpleMarkdownRules(
+ includeEscapeRule = false
+ )
+ )
+
+ SimpleRenderer.render(
+ source = it.content ?: "",
+ parser = parser,
+ initialState = MarkdownState(0),
+ renderContext = MarkdownContext(
+ memberMap = mapOf(),
+ userMap = RevoltAPI.userCache.toMap(),
+ channelMap = RevoltAPI.channelCache.mapValues { ch ->
+ ch.value.name ?: ch.value.id ?: "#DeletedChannel"
+ },
+ emojiMap = RevoltAPI.emojiCache,
+ serverId = channel.server ?: "",
)
)
-
- SimpleRenderer.render(
- source = it.content ?: "",
- parser = parser,
- initialState = MarkdownState(0),
- renderContext = MarkdownContext(
- memberMap = mapOf(),
- userMap = RevoltAPI.userCache.toMap(),
- channelMap = RevoltAPI.channelCache.mapValues { ch ->
- ch.value.name ?: ch.value.id ?: "#DeletedChannel"
- },
- emojiMap = RevoltAPI.emojiCache,
- serverId = channel.server ?: "",
- )
- )
- },
- onMessageContextMenu = {
- messageContextSheetShown = true
- messageContextSheetTarget = message.id ?: ""
- },
- onAvatarClick = {
- message.author?.let { author ->
- onUserSheetOpenFor(author, channel.server)
- }
- },
- canReply = true,
- onReply = {
- if (viewModel.pendingReplies.size >= 5) {
- Toast.makeText(
- context,
- context.getString(R.string.too_many_replies, 5),
- Toast.LENGTH_SHORT
- ).show()
- return@Message
- }
- viewModel.replyToMessage(message)
- },
- )
+ },
+ onMessageContextMenu = {
+ messageContextSheetShown = true
+ messageContextSheetTarget = message.id ?: ""
+ },
+ onAvatarClick = {
+ message.author?.let { author ->
+ onUserSheetOpenFor(author, channel.server)
+ }
+ },
+ canReply = true,
+ onReply = {
+ if (viewModel.pendingReplies.size >= 5) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.too_many_replies, 5),
+ Toast.LENGTH_SHORT
+ ).show()
+ return@Message
+ }
+ viewModel.replyToMessage(message)
+ },
+ )
+ }
}
item {
diff --git a/app/src/main/res/drawable/ic_account_arrow_left_24dp.xml b/app/src/main/res/drawable/ic_account_arrow_left_24dp.xml
new file mode 100644
index 00000000..63887410
--- /dev/null
+++ b/app/src/main/res/drawable/ic_account_arrow_left_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_account_arrow_right_24dp.xml b/app/src/main/res/drawable/ic_account_arrow_right_24dp.xml
new file mode 100644
index 00000000..0b609bce
--- /dev/null
+++ b/app/src/main/res/drawable/ic_account_arrow_right_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_account_cancel_24dp.xml b/app/src/main/res/drawable/ic_account_cancel_24dp.xml
new file mode 100644
index 00000000..238d9e6e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_account_cancel_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_account_plus_24dp.xml b/app/src/main/res/drawable/ic_account_plus_24dp.xml
new file mode 100644
index 00000000..833ca68d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_account_plus_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_cursor_text_24dp.xml b/app/src/main/res/drawable/ic_cursor_text_24dp.xml
new file mode 100644
index 00000000..3f0876ab
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cursor_text_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_gavel_24dp.xml b/app/src/main/res/drawable/ic_gavel_24dp.xml
new file mode 100644
index 00000000..7d5c4c46
--- /dev/null
+++ b/app/src/main/res/drawable/ic_gavel_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_image_multiple_24dp.xml b/app/src/main/res/drawable/ic_image_multiple_24dp.xml
new file mode 100644
index 00000000..589c7eed
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_multiple_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_key_arrow_right_24dp.xml b/app/src/main/res/drawable/ic_key_arrow_right_24dp.xml
new file mode 100644
index 00000000..a43b6126
--- /dev/null
+++ b/app/src/main/res/drawable/ic_key_arrow_right_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_shield_24dp.xml b/app/src/main/res/drawable/ic_shield_24dp.xml
new file mode 100644
index 00000000..50ca6055
--- /dev/null
+++ b/app/src/main/res/drawable/ic_shield_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_text_box_multiple_24dp.xml b/app/src/main/res/drawable/ic_text_box_multiple_24dp.xml
new file mode 100644
index 00000000..f45143ca
--- /dev/null
+++ b/app/src/main/res/drawable/ic_text_box_multiple_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 46c8444a..20066f0d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -134,6 +134,18 @@
Unknown message, tap to jump
Sent attachments
+ Ownership changed
+ Channel icon changed
+ Channel description changed
+ Channel renamed
+ User removed
+ User added
+ User banned
+ User kicked
+ User left
+ User joined
+ System message
+
Today
Yesterday
%1$d days ago