feat: support interactions
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
a8065151af
commit
c6cd473bd5
|
|
@ -21,6 +21,7 @@ data class Message(
|
||||||
val masquerade: Masquerade? = null,
|
val masquerade: Masquerade? = null,
|
||||||
val system: SystemInfo? = null,
|
val system: SystemInfo? = null,
|
||||||
val webhook: WebHook? = null,
|
val webhook: WebHook? = null,
|
||||||
|
val interactions: InteractionsDescription? = 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
|
||||||
) {
|
) {
|
||||||
|
|
@ -116,4 +117,10 @@ data class SystemInfo(
|
||||||
data class WebHook(
|
data class WebHook(
|
||||||
val avatar: String? = null,
|
val avatar: String? = null,
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class InteractionsDescription(
|
||||||
|
val reactions: List<String>? = null,
|
||||||
|
@SerialName("restrict_reactions") val restrictReactions: Boolean? = null
|
||||||
)
|
)
|
||||||
|
|
@ -518,16 +518,25 @@ fun Message(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((message.reactions?.size ?: 0) > 0) {
|
val reactionsAndInteractions = remember(message.reactions) {
|
||||||
|
message.reactions.orEmpty().toMutableMap().also {
|
||||||
|
message.interactions?.reactions?.forEach { reaction ->
|
||||||
|
if (!it.containsKey(reaction)) {
|
||||||
|
it[reaction] = listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reactionsAndInteractions.isNotEmpty()) {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
FlowRow(
|
FlowRow(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
message.reactions?.forEach { reaction ->
|
reactionsAndInteractions.forEach { reaction ->
|
||||||
Reaction(
|
Reaction(
|
||||||
reaction.key, reaction.value,
|
reaction.key, reaction.value,
|
||||||
onClick = { hasOwn ->
|
onClick = { hasOwn ->
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ScrollableTabRow
|
import androidx.compose.material3.PrimaryScrollableTabRow
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
|
@ -63,7 +64,19 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
|
||||||
val message = RevoltAPI.messageCache[messageId] ?: return
|
val message = RevoltAPI.messageCache[messageId] ?: return
|
||||||
val channel = RevoltAPI.channelCache[message.channel] ?: return
|
val channel = RevoltAPI.channelCache[message.channel] ?: return
|
||||||
val reactions = message.reactions
|
val reactions = message.reactions
|
||||||
val reactionEmoji = reactions?.keys?.toList()
|
val interactions = message.interactions?.reactions ?: emptyList()
|
||||||
|
val reactionEmoji =
|
||||||
|
(reactions?.keys?.toList() ?: emptyList())
|
||||||
|
.plus(interactions)
|
||||||
|
.distinct()
|
||||||
|
.filterNot { it.isEmpty() }
|
||||||
|
.sortedBy {
|
||||||
|
if (it.isUlid()) {
|
||||||
|
RevoltAPI.emojiCache[it]?.name ?: it.codePointAt(0).toString()
|
||||||
|
} else {
|
||||||
|
it.codePointAt(0).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
@ -81,60 +94,62 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
|
||||||
var selectedReactionIndex by remember(
|
var selectedReactionIndex by remember(
|
||||||
messageId,
|
messageId,
|
||||||
emoji
|
emoji
|
||||||
) { mutableIntStateOf(reactionEmoji?.indexOfFirst { it == emoji } ?: 0) }
|
) { mutableIntStateOf(reactionEmoji.indexOfFirst { it == emoji }) }
|
||||||
|
|
||||||
if (selectedReactionIndex >= (reactionEmoji?.size ?: 0)) {
|
if (selectedReactionIndex >= reactionEmoji.size || selectedReactionIndex < 0) {
|
||||||
selectedReactionIndex = 0
|
selectedReactionIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reactionEmoji?.isEmpty() == true) {
|
if (reactionEmoji.isEmpty()) {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
stickyHeader(key = "tabs") {
|
stickyHeader(key = "tabs") {
|
||||||
ScrollableTabRow(
|
if (reactionEmoji.isNotEmpty() && selectedReactionIndex < reactionEmoji.size) {
|
||||||
selectedTabIndex = selectedReactionIndex,
|
PrimaryScrollableTabRow(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
selectedTabIndex = selectedReactionIndex,
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
modifier = Modifier.fillMaxWidth(),
|
||||||
divider = {}
|
containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||||
) {
|
divider = {}
|
||||||
reactionEmoji?.forEachIndexed { index, emoji ->
|
) {
|
||||||
Tab(
|
reactionEmoji.forEachIndexed { index, emoji ->
|
||||||
text = {
|
Tab(
|
||||||
if (emoji.isUlid()) {
|
text = {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
if (emoji.isUlid()) {
|
||||||
RemoteImage(
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
url = "$REVOLT_FILES/emojis/${emoji}",
|
RemoteImage(
|
||||||
description = null,
|
url = "$REVOLT_FILES/emojis/${emoji}",
|
||||||
modifier = Modifier.size(16.dp)
|
description = null,
|
||||||
)
|
modifier = Modifier.size(16.dp)
|
||||||
Spacer(Modifier.size(6.dp))
|
)
|
||||||
|
Spacer(Modifier.size(6.dp))
|
||||||
|
Text(
|
||||||
|
"${reactions?.get(emoji)?.size ?: 0}",
|
||||||
|
style = LocalTextStyle.current.copy(fontFeatureSettings = "tnum")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Text(
|
Text(
|
||||||
"${reactions[emoji]?.size ?: 0}",
|
"$emoji ${reactions?.get(emoji)?.size ?: 0}",
|
||||||
style = LocalTextStyle.current.copy(fontFeatureSettings = "tnum")
|
style = LocalTextStyle.current.copy(fontFeatureSettings = "tnum")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
Text(
|
selected = selectedReactionIndex == index,
|
||||||
"$emoji ${reactions[emoji]?.size ?: 0}",
|
onClick = { selectedReactionIndex = index }
|
||||||
style = LocalTextStyle.current.copy(fontFeatureSettings = "tnum")
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
selected = selectedReactionIndex == index,
|
|
||||||
onClick = { selectedReactionIndex = index }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
HorizontalDivider()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reactionEmoji?.isNotEmpty() == true) {
|
item("info") {
|
||||||
item("info") {
|
if (reactionEmoji.isNotEmpty() == true) {
|
||||||
val current = reactionEmoji[selectedReactionIndex]
|
val current = reactionEmoji[selectedReactionIndex]
|
||||||
|
|
||||||
// Code related to enabling of experimental features
|
// <editor-fold desc="Code related to enabling of experimental features">
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val canBeUsedForTapCountIncrement =
|
val canBeUsedForTapCountIncrement =
|
||||||
remember(selectedReactionIndex) {
|
remember(selectedReactionIndex) {
|
||||||
|
|
@ -197,7 +212,7 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// End of code related to enabling of experimental features
|
// </editor-fold>
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
|
@ -297,35 +312,35 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val reactionsForEmoji = reactions[reactionEmoji[selectedReactionIndex]]
|
val reactionsForEmoji =
|
||||||
items(reactionsForEmoji?.size ?: 0) { index ->
|
reactions?.get(reactionEmoji[selectedReactionIndex]) ?: emptyList()
|
||||||
val reaction = reactionsForEmoji?.get(index) ?: return@items
|
items(items = reactionsForEmoji) { reaction ->
|
||||||
val userOrNull = RevoltAPI.userCache[reaction]
|
val userOrNull = RevoltAPI.userCache[reaction]
|
||||||
val user = userOrNull ?: User.getPlaceholder(reaction)
|
val user = userOrNull ?: User.getPlaceholder(reaction)
|
||||||
val member = if (channel.server != null && user.id != null) {
|
val member = if (channel.server != null && user.id != null) {
|
||||||
RevoltAPI.members.getMember(channel.server, user.id)
|
RevoltAPI.members.getMember(channel.server, user.id)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(reaction) {
|
LaunchedEffect(reaction) {
|
||||||
if (reaction !in RevoltAPI.userCache) {
|
if (reaction !in RevoltAPI.userCache) {
|
||||||
try {
|
try {
|
||||||
RevoltAPI.userCache[reaction] = fetchUser(reaction)
|
RevoltAPI.userCache[reaction] = fetchUser(reaction)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// too bad!
|
// too bad!
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MemberListItem(
|
|
||||||
member = member,
|
|
||||||
user = user,
|
|
||||||
serverId = channel.server,
|
|
||||||
userId = reaction,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemberListItem(
|
||||||
|
member = member,
|
||||||
|
user = user,
|
||||||
|
serverId = channel.server,
|
||||||
|
userId = reaction,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
item("bottom") {
|
item("bottom") {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue