feat: harmonise non-special sheets for m3

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-03-28 03:34:33 +01:00
parent c03b4410f4
commit cbabfe1ec0
16 changed files with 765 additions and 740 deletions

View File

@ -0,0 +1,66 @@
package chat.revolt.components.generic
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun SheetButton(
headlineContent: @Composable () -> Unit,
leadingContent: @Composable () -> Unit,
onClick: () -> Unit,
modifier: Modifier = Modifier,
supportingContent: @Composable (() -> Unit)? = null,
dangerous: Boolean = false
) {
Box(
modifier = modifier
.clickable { onClick() }
.padding(horizontal = 16.dp)
) {
ListItem(
headlineContent = {
CompositionLocalProvider(
value = if (dangerous) {
LocalContentColor provides MaterialTheme.colorScheme.error
} else {
LocalContentColor provides MaterialTheme.colorScheme.onSurface
}
) {
headlineContent()
}
},
leadingContent = {
CompositionLocalProvider(
value = if (dangerous) {
LocalContentColor provides MaterialTheme.colorScheme.error
} else {
LocalContentColor provides MaterialTheme.colorScheme.onSurface
}
) {
leadingContent()
}
},
supportingContent = {
supportingContent?.run {
CompositionLocalProvider(
value = if (dangerous) {
LocalContentColor provides MaterialTheme.colorScheme.error
} else {
LocalContentColor provides MaterialTheme.colorScheme.onSurface
}
) {
this()
}
}
}
)
}
}

View File

@ -1,73 +0,0 @@
package chat.revolt.components.generic
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
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.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun SheetClickable(
icon: @Composable (Modifier) -> Unit,
label: @Composable (TextStyle) -> Unit,
modifier: Modifier = Modifier,
dangerous: Boolean = false,
onClick: () -> Unit
) {
CompositionLocalProvider(
LocalContentColor provides if (dangerous) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onBackground
) {
Box(modifier = modifier.padding(vertical = 4.dp)) {
Row(
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
.clickable(onClick = onClick)
.padding(all = 4.dp)
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
icon(Modifier.padding(end = 16.dp))
label(
MaterialTheme.typography.bodyMedium.copy(
color = LocalContentColor.current,
fontWeight = FontWeight.SemiBold
)
)
}
}
}
}
@Preview(showBackground = true)
@Composable
fun SettingsCategoryPreview() {
SheetClickable(
icon = { modifier ->
Icon(
modifier = modifier,
imageVector = Icons.Default.Person,
contentDescription = "Account"
)
},
label = { textStyle ->
Text(text = "Account", style = textStyle)
},
onClick = {}
)
}

View File

@ -0,0 +1,12 @@
package chat.revolt.components.generic
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun SheetEnd(modifier: Modifier = Modifier) {
Spacer(modifier.height(8.dp))
}

View File

@ -12,6 +12,7 @@ import androidx.compose.ui.unit.dp
fun SheetHeaderPadding(content: @Composable BoxScope.() -> Unit) {
Box(
Modifier
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
.fillMaxWidth(), content = content)
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
.fillMaxWidth(), content = content
)
}

View File

@ -2,15 +2,23 @@ package chat.revolt.components.screens.chat
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -24,24 +32,35 @@ import chat.revolt.api.schemas.ChannelType
import chat.revolt.api.schemas.User
import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.markdown.MarkdownTree
import chat.revolt.ndk.AstNode
import chat.revolt.ndk.Stendal
@Composable
fun ChannelSheetHeader(
channelName: String,
channelIcon: AutumnResource? = null,
channelType: ChannelType,
channelDescription: String? = null,
dmPartner: User? = null
) {
var renderedChannelDescription by remember { mutableStateOf<AstNode?>(null) }
LaunchedEffect(channelDescription) {
if (channelDescription != null) {
renderedChannelDescription = Stendal.render(channelDescription)
}
}
Row(
modifier = Modifier.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.clip(if (channelType == ChannelType.DirectMessage) CircleShape else MaterialTheme.shapes.medium)
.background(
MaterialTheme.colorScheme.primary.copy(alpha = 0.2f)
MaterialTheme.colorScheme.primaryContainer
),
contentAlignment = Alignment.Center
) {
@ -49,11 +68,11 @@ fun ChannelSheetHeader(
RemoteImage(
url = "$REVOLT_FILES/icons/${channelIcon.id ?: ""}?max_side=48",
description = null, // decorative
contentScale = ContentScale.Fit,
contentScale = ContentScale.Crop,
height = 48,
width = 48,
modifier = Modifier
.size(24.dp)
.size(48.dp)
)
} else if (dmPartner != null) {
UserAvatar(
@ -66,17 +85,26 @@ fun ChannelSheetHeader(
.size(48.dp)
)
} else {
ChannelIcon(channelType = channelType)
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) {
ChannelIcon(channelType = channelType)
}
}
}
Spacer(modifier = Modifier.width(12.dp))
Text(
text = channelName,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Column {
Text(
text = channelName,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (renderedChannelDescription != null && channelDescription?.isNotBlank() == true) {
Spacer(modifier = Modifier.height(8.dp))
MarkdownTree(node = renderedChannelDescription!!)
}
}
}
}

View File

@ -5,7 +5,6 @@ import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
@ -16,7 +15,6 @@ import androidx.compose.material.icons.automirrored.filled.ExitToApp
import androidx.compose.material.icons.filled.Build
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@ -32,6 +30,8 @@ import chat.revolt.R
import chat.revolt.activities.InviteActivity
import chat.revolt.api.REVOLT_APP
import chat.revolt.components.generic.FormTextField
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.components.generic.SheetHeaderPadding
@Composable
@ -61,7 +61,7 @@ fun AddServerSheet() {
Spacer(modifier = Modifier.height(16.dp))
ListItem(
SheetButton(
headlineContent = {
Text(stringResource(id = R.string.add_server_sheet_join_by_invite))
},
@ -71,12 +71,12 @@ fun AddServerSheet() {
contentDescription = null
)
},
modifier = Modifier.clickable {
onClick = {
joinFromInviteModalOpen.value = true
}
)
ListItem(
SheetButton(
headlineContent = {
Text(stringResource(id = R.string.add_server_sheet_create_new))
},
@ -86,7 +86,7 @@ fun AddServerSheet() {
contentDescription = null
)
},
modifier = Modifier.clickable {
onClick = {
Toast.makeText(
context,
context.getString(
@ -97,7 +97,7 @@ fun AddServerSheet() {
}
)
Spacer(modifier = Modifier.height(16.dp))
SheetEnd()
}
}

View File

@ -2,7 +2,6 @@ package chat.revolt.sheets
import android.widget.Toast
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.CircularProgressIndicator
@ -20,7 +19,8 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
import chat.revolt.R
import chat.revolt.api.RevoltAPI
import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.internals.Platform
import kotlinx.coroutines.launch
@ -43,23 +43,20 @@ fun ChannelContextSheet(channelId: String, onHideSheet: suspend () -> Unit) {
val coroutineScope = rememberCoroutineScope()
Column {
SheetClickable(
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.channel_context_sheet_actions_copy_id),
style = style
)
}
) {
if (channel.id == null) return@SheetClickable
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.channel_context_sheet_actions_copy_id),
)
},
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
contentDescription = null
)
},
onClick = {
if (channel.id == null) return@SheetButton
clipboardManager.setText(AnnotatedString(channel.id))
@ -75,22 +72,21 @@ fun ChannelContextSheet(channelId: String, onHideSheet: suspend () -> Unit) {
onHideSheet()
}
}
)
SheetClickable(
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_eye_check_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.channel_context_sheet_actions_mark_read),
style = style
)
}
) {
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.channel_context_sheet_actions_mark_read),
)
},
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_eye_check_24dp),
contentDescription = null
)
},
onClick = {
coroutineScope.launch {
channel.lastMessageID?.let {
RevoltAPI.unreads.markAsRead(channelId, it, sync = true)
@ -98,5 +94,7 @@ fun ChannelContextSheet(channelId: String, onHideSheet: suspend () -> Unit) {
onHideSheet()
}
}
}
)
SheetEnd()
}

View File

@ -1,13 +1,11 @@
package chat.revolt.sheets
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.filled.Add
@ -15,8 +13,8 @@ import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
@ -40,7 +38,8 @@ import chat.revolt.api.internals.has
import chat.revolt.api.schemas.ChannelType
import chat.revolt.callbacks.Action
import chat.revolt.callbacks.ActionChannel
import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.components.screens.chat.ChannelSheetHeader
import chat.revolt.internals.extensions.rememberChannelPermissions
import chat.revolt.screens.chat.dialogs.InviteDialog
@ -98,169 +97,130 @@ fun ChannelInfoSheet(channelId: String, onHideSheet: suspend () -> Unit) {
return
}
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
.verticalScroll(rememberScrollState())
) {
val isDM = ChannelUtils.resolveDMPartner(channel) != null
val partner = ChannelUtils
.resolveDMPartner(channel)
?.let {
RevoltAPI.userCache[it]
}
val partner = ChannelUtils
.resolveDMPartner(channel)
?.let {
RevoltAPI.userCache[it]
}
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 4.dp),
) {
ChannelSheetHeader(
channelName = channel.name
?: ChannelUtils.resolveDMName(channel)
?: stringResource(id = R.string.unknown),
channelIcon = channel.icon,
channelType = channel.channelType ?: ChannelType.TextChannel,
channelDescription = channel.description,
dmPartner = partner
)
HorizontalDivider()
}
if (!isDM) {
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(id = R.string.channel_info_sheet_description),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(bottom = 10.dp)
)
Text(
text = if (channel.description.isNullOrBlank()) {
stringResource(
id = R.string.channel_info_sheet_description_empty
when (channel.channelType) {
ChannelType.TextChannel, ChannelType.VoiceChannel, ChannelType.Group -> {
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.channel_info_sheet_options_members),
)
} else {
channel.description
},
modifier = Modifier.padding(bottom = 10.dp)
leadingContent = {
Icon(
imageVector = Icons.AutoMirrored.Default.List,
contentDescription = null
)
},
onClick = {
memberListSheetShown = true
}
)
}
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(id = R.string.channel_info_sheet_options),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(bottom = 4.dp)
)
else -> {}
}
if (
Roles.permissionFor(
channel,
RevoltAPI.userCache[RevoltAPI.selfId]
) has PermissionBit.InviteOthers
) {
when (channel.channelType) {
ChannelType.TextChannel, ChannelType.VoiceChannel, ChannelType.Group -> {
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.AutoMirrored.Default.List,
contentDescription = null,
modifier = modifier
ChannelType.TextChannel, ChannelType.VoiceChannel -> {
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.channel_info_sheet_options_invite),
)
},
label = { style ->
Text(
text = stringResource(id = R.string.channel_info_sheet_options_members),
style = style
leadingContent = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = null
)
},
onClick = {
inviteDialogShown = true
}
) {
memberListSheetShown = true
}
)
}
ChannelType.Group -> {
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.channel_info_sheet_options_add),
)
},
leadingContent = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = null
)
},
onClick = {}
)
}
else -> {}
}
}
if (
Roles.permissionFor(
channel,
RevoltAPI.userCache[RevoltAPI.selfId]
) has PermissionBit.InviteOthers
) {
when (channel.channelType) {
ChannelType.TextChannel, ChannelType.VoiceChannel -> {
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.channel_info_sheet_options_invite),
style = style
)
}
) {
inviteDialogShown = true
}
}
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.channel_info_sheet_options_notifications_manage),
)
},
leadingContent = {
Icon(
imageVector = Icons.Default.Notifications,
contentDescription = null
)
},
onClick = {}
)
ChannelType.Group -> {
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.channel_info_sheet_options_add),
style = style
)
}
) {
}
}
else -> {}
}
}
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.Default.Notifications,
contentDescription = null,
modifier = modifier
if (
(permissions has PermissionBit.ManageChannel || permissions has PermissionBit.ManageRole)
&& (channel.channelType != ChannelType.SavedMessages && channel.channelType != ChannelType.DirectMessage)
) {
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.settings),
)
},
label = { style ->
Text(
text = stringResource(
id = R.string.channel_info_sheet_options_notifications_manage
),
style = style
leadingContent = {
Icon(
imageVector = Icons.Default.Settings,
contentDescription = null
)
}
) {
}
if (
(permissions has PermissionBit.ManageChannel || permissions has PermissionBit.ManageRole)
&& (channel.channelType != ChannelType.SavedMessages && channel.channelType != ChannelType.DirectMessage)
) {
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.Default.Settings,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(
id = R.string.settings
),
style = style
)
}
) {
},
onClick = {
scope.launch {
onHideSheet()
}
@ -269,8 +229,8 @@ fun ChannelInfoSheet(channelId: String, onHideSheet: suspend () -> Unit) {
ActionChannel.send(Action.TopNavigate("settings/channel/${channel.id}"))
}
}
}
Spacer(modifier = Modifier.height(8.dp))
)
}
SheetEnd()
}

View File

@ -1,20 +1,17 @@
package chat.revolt.sheets
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
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.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.width
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -24,7 +21,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
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.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
@ -41,7 +37,8 @@ import chat.revolt.api.routes.custom.fetchEmoji
import chat.revolt.api.schemas.Emoji
import chat.revolt.api.schemas.Server
import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.internals.Platform
import kotlinx.coroutines.launch
@ -62,40 +59,31 @@ fun EmoteInfoSheet(id: String, onDismiss: () -> Unit) {
}
Column(
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState())
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 4.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(0.dp))
verticalAlignment = Alignment.CenterVertically
) {
RemoteImage(
url = "$REVOLT_FILES/emojis/$id",
description = emoteInfo?.name,
contentScale = ContentScale.Fit,
modifier = Modifier
.padding(16.dp)
.size(32.dp)
)
Column(
modifier = Modifier.padding(
top = 16.dp,
start = 0.dp,
end = 16.dp,
bottom = 16.dp
)
) {
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(
text = emoteInfo?.name ?: id,
fontWeight = FontWeight.Bold,
text = ":${emoteInfo?.name ?: id}:",
fontWeight = FontWeight.SemiBold,
letterSpacing = 1.15.sp
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (parentServer != null) {
stringResource(
@ -109,23 +97,22 @@ fun EmoteInfoSheet(id: String, onDismiss: () -> Unit) {
}
}
Spacer(modifier = Modifier.height(8.dp))
HorizontalDivider()
}
SheetClickable(
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.copy),
style = style
)
}
) {
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.copy)
)
},
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_24dp),
contentDescription = null
)
},
onClick = {
coroutineScope.launch {
clipboardManager.setText(AnnotatedString(":$id:"))
if (Platform.needsShowClipboardNotification()) {
@ -138,5 +125,7 @@ fun EmoteInfoSheet(id: String, onDismiss: () -> Unit) {
}
onDismiss()
}
}
)
SheetEnd()
}

View File

@ -3,24 +3,23 @@ package chat.revolt.sheets
import android.net.Uri
import android.widget.Toast
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ExitToApp
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
@ -28,8 +27,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
import chat.revolt.R
import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.internals.Platform
import chat.revolt.ui.theme.ClearRippleTheme
import kotlinx.coroutines.launch
@Composable
@ -39,50 +40,86 @@ fun LinkInfoSheet(url: String, onDismiss: () -> Unit) {
val coroutineScope = rememberCoroutineScope()
Column(
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState())
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 4.dp),
) {
Box(
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(0.dp))
.clickable(onClick = {
if (url.startsWith("revolt-android://")) return@clickable
CompositionLocalProvider(value = LocalRippleTheme provides ClearRippleTheme) {
Box(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = {
val customTab = CustomTabsIntent
.Builder()
.setShowTitle(true)
.build()
val customTab = CustomTabsIntent
.Builder()
.setShowTitle(true)
.build()
customTab.launchUrl(context, Uri.parse(url))
})
) {
Text(
text = url,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(16.dp)
)
}
Spacer(modifier = Modifier.height(8.dp))
SheetClickable(
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
try {
customTab.launchUrl(context, Uri.parse(url))
} catch (e: Exception) {
Toast
.makeText(
context,
context.getString(R.string.link_type_no_intent),
Toast.LENGTH_SHORT
)
.show()
}
})
) {
Text(
text = stringResource(id = R.string.copy),
style = style
text = url,
color = MaterialTheme.colorScheme.primary
)
}
) {
}
HorizontalDivider()
}
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.link_open)
)
},
leadingContent = {
Icon(
imageVector = Icons.AutoMirrored.Default.ExitToApp,
contentDescription = null
)
},
onClick = {
val customTab = CustomTabsIntent
.Builder()
.setShowTitle(true)
.build()
try {
customTab.launchUrl(context, Uri.parse(url))
} catch (e: Exception) {
Toast
.makeText(
context,
context.getString(R.string.link_type_no_intent),
Toast.LENGTH_SHORT
)
.show()
}
}
)
SheetButton(
headlineContent = {
Text(
text = stringResource(id = R.string.copy)
)
},
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_24dp),
contentDescription = null
)
},
onClick = {
coroutineScope.launch {
clipboardManager.setText(AnnotatedString(url))
if (Platform.needsShowClipboardNotification()) {
@ -95,5 +132,7 @@ fun LinkInfoSheet(url: String, onDismiss: () -> Unit) {
}
onDismiss()
}
}
)
SheetEnd()
}

View File

@ -1,10 +1,8 @@
package chat.revolt.sheets
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@ -12,7 +10,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
@ -22,6 +19,8 @@ import androidx.compose.ui.text.style.TextOverflow
import chat.revolt.R
import chat.revolt.api.RevoltAPI
import chat.revolt.api.routes.channel.removeMember
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.internals.Platform
import kotlinx.coroutines.launch
@ -46,7 +45,7 @@ fun ColumnScope.GroupDMMemberContextSheet(
if (channel == null) return
if (channel.owner == RevoltAPI.selfId && userId != RevoltAPI.selfId) {
ListItem(
SheetButton(
headlineContent = {
CompositionLocalProvider(value = LocalContentColor provides MaterialTheme.colorScheme.error) {
Text(
@ -67,7 +66,7 @@ fun ColumnScope.GroupDMMemberContextSheet(
)
}
},
modifier = Modifier.clickable {
onClick = {
scope.launch {
removeMember(channelId, userId)
onRequestUpdateMembers()
@ -78,7 +77,7 @@ fun ColumnScope.GroupDMMemberContextSheet(
}
// TODO replace with something useful (currently so that your sheet is not empty if you don't have permissions)
ListItem(
SheetButton(
headlineContent = {
Text(stringResource(R.string.user_info_sheet_copy_id))
},
@ -88,7 +87,7 @@ fun ColumnScope.GroupDMMemberContextSheet(
contentDescription = null
)
},
modifier = Modifier.clickable {
onClick = {
clipboardManager.setText(AnnotatedString(userId))
if (Platform.needsShowClipboardNotification()) {
@ -100,6 +99,8 @@ fun ColumnScope.GroupDMMemberContextSheet(
}
}
)
SheetEnd()
}
@Composable
@ -126,7 +127,7 @@ fun ColumnScope.ServerMemberContextSheet(
// TODO add something useful (moderation actions)
// TODO replace with something useful (currently so that your sheet is not empty if you don't have permissions)
ListItem(
SheetButton(
headlineContent = {
Text(stringResource(R.string.user_info_sheet_copy_id))
},
@ -136,7 +137,7 @@ fun ColumnScope.ServerMemberContextSheet(
contentDescription = null
)
},
modifier = Modifier.clickable {
onClick = {
clipboardManager.setText(AnnotatedString(userId))
if (Platform.needsShowClipboardNotification()) {
@ -148,4 +149,6 @@ fun ColumnScope.ServerMemberContextSheet(
}
}
)
SheetEnd()
}

View File

@ -1,10 +1,9 @@
package chat.revolt.sheets
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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
@ -13,18 +12,19 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -32,7 +32,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
@ -49,8 +48,10 @@ import chat.revolt.api.routes.channel.deleteMessage
import chat.revolt.api.routes.channel.react
import chat.revolt.callbacks.UiCallbacks
import chat.revolt.components.chat.Message
import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.internals.Platform
import chat.revolt.ui.theme.ClearRippleTheme
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@ -91,28 +92,70 @@ fun MessageContextSheet(
) {
Column(
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState())
) {
SheetClickable(
icon = { modifier ->
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_24dp),
contentDescription = null,
modifier = modifier
contentDescription = null
)
},
label = { style ->
headlineContent = {
Text(
text = stringResource(id = R.string.message_context_sheet_actions_copy),
style = style
text = stringResource(id = R.string.message_context_sheet_actions_copy)
)
}
) {
if (message.content.isNullOrEmpty()) {
},
onClick = {
if (message.content.isNullOrEmpty()) {
coroutineScope.launch {
shareSheetState.hide()
onHideSheet()
Toast.makeText(
context,
context.getString(
R.string.message_context_sheet_actions_copy_failed_empty
),
Toast.LENGTH_SHORT
).show()
}
return@SheetButton
}
if (Platform.needsShowClipboardNotification()) {
Toast.makeText(
context,
context.getString(R.string.copied),
Toast.LENGTH_SHORT
).show()
}
coroutineScope.launch {
shareSheetState.hide()
}
coroutineScope.launch {
clipboardManager.setText(AnnotatedString(message.content))
onHideSheet()
}
}
)
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_link_variant_24dp),
contentDescription = null
)
},
headlineContent = {
Text(
text = stringResource(
id = R.string.message_context_sheet_actions_copy_link
)
)
},
onClick = {
if (message.content.isNullOrEmpty()) {
Toast.makeText(
context,
context.getString(
@ -120,52 +163,33 @@ fun MessageContextSheet(
),
Toast.LENGTH_SHORT
).show()
coroutineScope.launch {
shareSheetState.hide()
}
coroutineScope.launch {
onHideSheet()
}
return@SheetButton
}
return@SheetClickable
}
if (Platform.needsShowClipboardNotification()) {
Toast.makeText(
context,
context.getString(R.string.copied),
Toast.LENGTH_SHORT
).show()
}
val server = RevoltAPI.serverCache.values.find { server ->
server.channels?.contains(message.channel) ?: false
}
val messageLink =
"$REVOLT_APP/server/${server?.id}/channel/${message.channel}/${message.id}"
coroutineScope.launch {
shareSheetState.hide()
}
coroutineScope.launch {
clipboardManager.setText(AnnotatedString(message.content))
onHideSheet()
}
}
SheetClickable(
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_link_variant_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(
id = R.string.message_context_sheet_actions_copy_link
),
style = style
)
}
) {
if (message.content.isNullOrEmpty()) {
Toast.makeText(
context,
context.getString(
R.string.message_context_sheet_actions_copy_failed_empty
),
Toast.LENGTH_SHORT
).show()
clipboardManager.setText(AnnotatedString(messageLink))
if (Platform.needsShowClipboardNotification()) {
Toast.makeText(
context,
context.getString(
R.string.message_context_sheet_actions_copy_link_copied
),
Toast.LENGTH_SHORT
).show()
}
coroutineScope.launch {
shareSheetState.hide()
@ -173,74 +197,49 @@ fun MessageContextSheet(
coroutineScope.launch {
onHideSheet()
}
return@SheetClickable
}
)
val server = RevoltAPI.serverCache.values.find { server ->
server.channels?.contains(message.channel) ?: false
}
val messageLink =
"$REVOLT_APP/server/${server?.id}/channel/${message.channel}/${message.id}"
clipboardManager.setText(AnnotatedString(messageLink))
if (Platform.needsShowClipboardNotification()) {
Toast.makeText(
context,
context.getString(
R.string.message_context_sheet_actions_copy_link_copied
),
Toast.LENGTH_SHORT
).show()
}
coroutineScope.launch {
shareSheetState.hide()
}
coroutineScope.launch {
onHideSheet()
}
}
SheetClickable(
icon = { modifier ->
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
contentDescription = null,
modifier = modifier
contentDescription = null
)
},
label = { style ->
headlineContent = {
Text(
text = stringResource(
id = R.string.message_context_sheet_actions_copy_id
),
style = style
)
)
}
) {
if (message.id == null) return@SheetClickable
},
onClick = {
if (message.id == null) return@SheetButton
clipboardManager.setText(AnnotatedString(message.id))
clipboardManager.setText(AnnotatedString(message.id))
if (Platform.needsShowClipboardNotification()) {
Toast.makeText(
context,
context.getString(
R.string.message_context_sheet_actions_copy_id_copied
),
Toast.LENGTH_SHORT
).show()
}
if (Platform.needsShowClipboardNotification()) {
Toast.makeText(
context,
context.getString(
R.string.message_context_sheet_actions_copy_id_copied
),
Toast.LENGTH_SHORT
).show()
}
coroutineScope.launch {
shareSheetState.hide()
coroutineScope.launch {
shareSheetState.hide()
}
coroutineScope.launch {
onHideSheet()
}
}
coroutineScope.launch {
onHideSheet()
}
}
)
}
SheetEnd()
}
}
@ -312,130 +311,124 @@ fun MessageContextSheet(
Column(
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState())
) {
Box(
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(0.dp))
.padding(bottom = 8.dp)
) {
Message(
message = message.copy(
tail = false,
masquerade = null
CompositionLocalProvider(value = LocalRippleTheme provides ClearRippleTheme) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 4.dp),
) {
Message(
message = message.copy(
tail = false,
masquerade = null
)
)
)
HorizontalDivider()
}
}
Spacer(modifier = Modifier.height(8.dp))
SheetClickable(
icon = { modifier ->
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_reply_24dp),
contentDescription = null,
modifier = modifier
contentDescription = null
)
},
label = { style ->
headlineContent = {
Text(
text = stringResource(id = R.string.message_context_sheet_actions_reply),
style = style
)
}
) {
coroutineScope.launch {
UiCallbacks.replyToMessage(messageId)
onHideSheet()
}
}
SheetClickable(
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_hamburger_plus_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.message_context_sheet_actions_react),
style = style
)
}
) {
showReactSheet = true
}
if (message.author == RevoltAPI.selfId) {
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.Default.Edit,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.message_context_sheet_actions_edit),
style = style
)
}
) {
onClick = {
coroutineScope.launch {
UiCallbacks.editMessage(messageId)
UiCallbacks.replyToMessage(messageId)
onHideSheet()
}
}
}
)
SheetClickable(
icon = { modifier ->
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_eye_off_24dp),
contentDescription = null,
modifier = modifier
painter = painterResource(id = R.drawable.ic_hamburger_plus_24dp),
contentDescription = null
)
},
label = { style ->
headlineContent = {
Text(
text = stringResource(id = R.string.message_context_sheet_actions_mark_unread),
style = style
text = stringResource(id = R.string.message_context_sheet_actions_react),
)
},
onClick = {
showReactSheet = true
}
) {
Toast.makeText(
context,
context.getString(R.string.comingsoon_toast),
Toast.LENGTH_SHORT
).show()
)
coroutineScope.launch {
onHideSheet()
}
if (message.author == RevoltAPI.selfId) {
SheetButton(
leadingContent = {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = null
)
},
headlineContent = {
Text(
text = stringResource(id = R.string.message_context_sheet_actions_edit),
)
},
onClick = {
coroutineScope.launch {
UiCallbacks.editMessage(messageId)
onHideSheet()
}
}
)
}
SheetClickable(
icon = { modifier ->
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_eye_off_24dp),
contentDescription = null
)
},
headlineContent = {
Text(
text = stringResource(id = R.string.message_context_sheet_actions_mark_unread),
)
},
onClick = {
Toast.makeText(
context,
context.getString(R.string.comingsoon_toast),
Toast.LENGTH_SHORT
).show()
coroutineScope.launch {
onHideSheet()
}
}
)
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_share_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
headlineContent = {
Text(
text = stringResource(id = R.string.share),
style = style
)
},
onClick = {
showShareSheet = true
}
) {
showShareSheet = true
}
)
if (
(message.channel?.let {
@ -446,47 +439,47 @@ fun MessageContextSheet(
)
} ?: 0) has PermissionBit.ManageMessages || message.author == RevoltAPI.selfId
) {
SheetClickable(
icon = { modifier ->
SheetButton(
leadingContent = {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = null,
modifier = modifier
contentDescription = null
)
},
label = { style ->
headlineContent = {
Text(
text = stringResource(id = R.string.message_context_sheet_actions_delete),
style = style
)
},
dangerous = true
) {
showDeleteMessageConfirmation = true
}
dangerous = true,
onClick = {
showDeleteMessageConfirmation = true
}
)
}
if (message.author != RevoltAPI.selfId) {
SheetClickable(
icon = { modifier ->
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_flag_24dp),
contentDescription = null,
modifier = modifier
contentDescription = null
)
},
label = { style ->
headlineContent = {
Text(
text = stringResource(id = R.string.message_context_sheet_actions_report),
style = style
)
},
dangerous = true
) {
coroutineScope.launch {
onReportMessage()
}
}
dangerous = true,
onClick = {
coroutineScope.launch {
onReportMessage()
}
},
)
}
SheetEnd()
}
}

View File

@ -1,14 +1,16 @@
package chat.revolt.sheets
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
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.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.LocalTextStyle
@ -16,7 +18,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -26,7 +27,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.res.stringResource
import androidx.compose.ui.text.PlatformTextStyle
@ -45,6 +45,7 @@ import chat.revolt.api.schemas.Emoji
import chat.revolt.api.schemas.User
import chat.revolt.components.chat.MemberListItem
import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.SheetEnd
@OptIn(ExperimentalFoundationApi::class)
@Composable
@ -118,94 +119,97 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
if (reactionEmoji?.isNotEmpty() == true) {
item("info") {
val current = reactionEmoji[selectedReactionIndex]
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(16.dp)
.clip(MaterialTheme.shapes.medium)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(0.dp))
) {
if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current }
RemoteImage(
url = "$REVOLT_FILES/emojis/$current/emoji.gif",
description = cached?.name,
contentScale = ContentScale.Fit,
modifier = Modifier
.padding(16.dp)
.size(32.dp)
)
} else {
Box(
modifier = Modifier
.padding(16.dp)
.size(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = current,
style = MaterialTheme.typography.bodyLarge.copy(
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
platformStyle = PlatformTextStyle(
includeFontPadding = false
)
),
modifier = Modifier
.size(64.dp)
)
}
}
Column(
modifier = Modifier.padding(
top = 16.dp,
start = 0.dp,
end = 16.dp,
bottom = 16.dp
)
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(
top = 16.dp,
start = 16.dp,
end = 16.dp,
bottom = 4.dp
),
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current }
Text(
text = ":${cached?.name ?: current}:",
fontWeight = FontWeight.Bold,
letterSpacing = 1.15.sp
RemoteImage(
url = "$REVOLT_FILES/emojis/$current/emoji.gif",
description = cached?.name,
contentScale = ContentScale.Fit,
modifier = Modifier
.size(32.dp)
)
} else {
Text(
text = MessageProcessor.emoji.unicodeAsShortcode(current)
?: current,
fontWeight = FontWeight.Bold,
letterSpacing = 1.15.sp
)
Box(
modifier = Modifier
.size(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = current,
style = MaterialTheme.typography.bodyLarge.copy(
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
platformStyle = PlatformTextStyle(
includeFontPadding = false
)
),
modifier = Modifier
.size(64.dp)
)
}
}
Text(
text = if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current }
if (cached?.parent != null) {
when (cached.parent.type) {
"Server" -> RevoltAPI.serverCache[cached.parent.id]?.name?.let {
stringResource(
id = R.string.emote_info_from_server,
it
)
}
?: stringResource(id = R.string.emote_info_from_server_unknown)
Spacer(modifier = Modifier.width(16.dp))
else -> stringResource(id = R.string.emote_info_from_server_unknown)
Column {
if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current }
Text(
text = ":${cached?.name ?: current}:",
fontWeight = FontWeight.SemiBold,
letterSpacing = 1.15.sp
)
} else {
Text(
text = MessageProcessor.emoji.unicodeAsShortcode(current)
?: current,
fontWeight = FontWeight.SemiBold,
letterSpacing = 1.15.sp
)
}
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current }
if (cached?.parent != null) {
when (cached.parent.type) {
"Server" -> RevoltAPI.serverCache[cached.parent.id]?.name?.let {
stringResource(
id = R.string.emote_info_from_server,
it
)
}
?: stringResource(id = R.string.emote_info_from_server_unknown)
else -> stringResource(id = R.string.emote_info_from_server_unknown)
}
} else {
stringResource(id = R.string.emote_info_from_server_unknown)
}
} else {
stringResource(id = R.string.emote_info_from_server_unknown)
stringResource(id = R.string.emote_info_from_unicode)
}
} else {
stringResource(id = R.string.emote_info_from_unicode)
}
)
)
}
}
HorizontalDivider()
}
}
@ -236,7 +240,7 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
}
item("bottom") {
Spacer(Modifier.size(16.dp))
SheetEnd()
}
}
}

View File

@ -1,18 +1,20 @@
package chat.revolt.sheets
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@ -32,7 +34,8 @@ import androidx.compose.ui.unit.dp
import chat.revolt.R
import chat.revolt.api.RevoltAPI
import chat.revolt.api.routes.server.leaveOrDeleteServer
import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.components.markdown.RichMarkdown
import chat.revolt.components.screens.settings.ServerOverview
import chat.revolt.internals.Platform
@ -138,19 +141,12 @@ fun ServerContextSheet(
}
Column(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 0.dp, bottom = 16.dp)
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 4.dp),
) {
ServerOverview(server)
Column(
modifier = Modifier.padding(horizontal = 4.dp)
) {
Text(
text = stringResource(id = R.string.server_context_sheet_category_description),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(vertical = 14.dp)
)
SelectionContainer {
RichMarkdown(
input = if (server.description?.isBlank() == false) {
server.description
@ -160,27 +156,25 @@ fun ServerContextSheet(
)
}
)
Text(
text = stringResource(id = R.string.server_context_sheet_category_actions),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 14.dp, bottom = 10.dp)
)
}
SheetClickable(icon = {
HorizontalDivider()
}
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
contentDescription = null,
modifier = it
contentDescription = null
)
}, label = {
},
headlineContent = {
Text(
text = stringResource(id = R.string.server_context_sheet_actions_copy_id),
style = it
text = stringResource(id = R.string.server_context_sheet_actions_copy_id)
)
}) {
if (server.id == null) return@SheetClickable
},
onClick = {
if (server.id == null) return@SheetButton
clipboardManager.setText(AnnotatedString(server.id))
@ -196,19 +190,21 @@ fun ServerContextSheet(
onHideSheet()
}
}
)
SheetClickable(icon = {
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_eye_check_24dp),
contentDescription = null,
modifier = it
contentDescription = null
)
}, label = {
},
headlineContent = {
Text(
text = stringResource(id = R.string.server_context_sheet_actions_mark_read),
style = it
text = stringResource(id = R.string.server_context_sheet_actions_mark_read)
)
}) {
},
onClick = {
coroutineScope.launch {
server.id?.let {
RevoltAPI.unreads.markServerAsRead(it, sync = true)
@ -216,37 +212,45 @@ fun ServerContextSheet(
onHideSheet()
}
}
)
if (server.owner != RevoltAPI.selfId) {
SheetClickable(icon = {
if (server.owner != RevoltAPI.selfId) {
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_flag_24dp),
contentDescription = null,
modifier = it
contentDescription = null
)
}, label = {
},
headlineContent = {
Text(
text = stringResource(id = R.string.server_context_sheet_actions_report),
style = it
)
}, dangerous = true) {
},
dangerous = true,
onClick = {
onReportServer()
}
)
SheetClickable(icon = {
SheetButton(
leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_left_bold_box_24dp),
contentDescription = null,
modifier = it
)
}, label = {
},
headlineContent = {
Text(
text = stringResource(id = R.string.server_context_sheet_actions_leave),
style = it
text = stringResource(id = R.string.server_context_sheet_actions_leave)
)
}, dangerous = true) {
},
dangerous = true,
onClick = {
showLeaveConfirmation = true
}
}
)
}
SheetEnd()
}

View File

@ -19,7 +19,8 @@ import androidx.compose.ui.unit.dp
import chat.revolt.R
import chat.revolt.api.RevoltAPI
import chat.revolt.api.routes.user.patchSelf
import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.components.generic.asApiName
import chat.revolt.components.generic.presenceFromStatus
import chat.revolt.components.screens.settings.UserOverview
@ -52,24 +53,25 @@ fun StatusSheet(onBeforeNavigation: () -> Unit, onGoSettings: () -> Unit) {
)
Spacer(modifier = Modifier.height(8.dp))
}
SheetClickable(
icon = { modifier ->
Icon(
imageVector = Icons.Default.Settings,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text(
text = stringResource(id = R.string.settings),
style = style
)
}
) {
SheetButton(
leadingContent = {
Icon(
imageVector = Icons.Default.Settings,
contentDescription = null
)
},
headlineContent = {
Text(
text = stringResource(id = R.string.settings)
)
},
onClick = {
onBeforeNavigation()
onGoSettings()
}
}
)
SheetEnd()
}

View File

@ -289,9 +289,7 @@
<string name="channel_context_sheet_actions_copy_id_copied">Copied channel ID to clipboard</string>
<string name="channel_context_sheet_actions_mark_read">Mark as read</string>
<string name="server_context_sheet_category_description">Description</string>
<string name="server_context_sheet_description_empty">There hasn\'t been a description set for this server yet.</string>
<string name="server_context_sheet_category_actions">Actions</string>
<string name="server_context_sheet_actions_copy_id">Copy ID</string>
<string name="server_context_sheet_actions_copy_id_copied">Copied server ID to clipboard</string>
@ -416,6 +414,7 @@
<string name="report_block_no">Don\'t block</string>
<string name="link_type_no_intent">No app found to handle this link</string>
<string name="link_open">Open</string>
<string name="invite_message">You\'ve been invited to join this server. Would you like to join?</string>
<string name="invite_join">Join</string>