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) { fun SheetHeaderPadding(content: @Composable BoxScope.() -> Unit) {
Box( Box(
Modifier Modifier
.padding(start = 16.dp, end = 16.dp, top = 16.dp) .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
.fillMaxWidth(), content = content) .fillMaxWidth(), content = content
)
} }

View File

@ -2,15 +2,23 @@ package chat.revolt.components.screens.chat
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer 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.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.api.schemas.User
import chat.revolt.components.generic.RemoteImage import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.UserAvatar import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.markdown.MarkdownTree
import chat.revolt.ndk.AstNode
import chat.revolt.ndk.Stendal
@Composable @Composable
fun ChannelSheetHeader( fun ChannelSheetHeader(
channelName: String, channelName: String,
channelIcon: AutumnResource? = null, channelIcon: AutumnResource? = null,
channelType: ChannelType, channelType: ChannelType,
channelDescription: String? = null,
dmPartner: User? = null dmPartner: User? = null
) { ) {
var renderedChannelDescription by remember { mutableStateOf<AstNode?>(null) }
LaunchedEffect(channelDescription) {
if (channelDescription != null) {
renderedChannelDescription = Stendal.render(channelDescription)
}
}
Row( Row(
modifier = Modifier.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(48.dp) .size(48.dp)
.clip(CircleShape) .clip(if (channelType == ChannelType.DirectMessage) CircleShape else MaterialTheme.shapes.medium)
.background( .background(
MaterialTheme.colorScheme.primary.copy(alpha = 0.2f) MaterialTheme.colorScheme.primaryContainer
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
@ -49,11 +68,11 @@ fun ChannelSheetHeader(
RemoteImage( RemoteImage(
url = "$REVOLT_FILES/icons/${channelIcon.id ?: ""}?max_side=48", url = "$REVOLT_FILES/icons/${channelIcon.id ?: ""}?max_side=48",
description = null, // decorative description = null, // decorative
contentScale = ContentScale.Fit, contentScale = ContentScale.Crop,
height = 48, height = 48,
width = 48, width = 48,
modifier = Modifier modifier = Modifier
.size(24.dp) .size(48.dp)
) )
} else if (dmPartner != null) { } else if (dmPartner != null) {
UserAvatar( UserAvatar(
@ -66,17 +85,26 @@ fun ChannelSheetHeader(
.size(48.dp) .size(48.dp)
) )
} else { } else {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) {
ChannelIcon(channelType = channelType) ChannelIcon(channelType = channelType)
} }
} }
}
Spacer(modifier = Modifier.width(12.dp)) Spacer(modifier = Modifier.width(12.dp))
Column {
Text( Text(
text = channelName, text = channelName,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis 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 android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height 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.material.icons.filled.Build
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@ -32,6 +30,8 @@ import chat.revolt.R
import chat.revolt.activities.InviteActivity import chat.revolt.activities.InviteActivity
import chat.revolt.api.REVOLT_APP import chat.revolt.api.REVOLT_APP
import chat.revolt.components.generic.FormTextField import chat.revolt.components.generic.FormTextField
import chat.revolt.components.generic.SheetButton
import chat.revolt.components.generic.SheetEnd
import chat.revolt.components.generic.SheetHeaderPadding import chat.revolt.components.generic.SheetHeaderPadding
@Composable @Composable
@ -61,7 +61,7 @@ fun AddServerSheet() {
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
ListItem( SheetButton(
headlineContent = { headlineContent = {
Text(stringResource(id = R.string.add_server_sheet_join_by_invite)) Text(stringResource(id = R.string.add_server_sheet_join_by_invite))
}, },
@ -71,12 +71,12 @@ fun AddServerSheet() {
contentDescription = null contentDescription = null
) )
}, },
modifier = Modifier.clickable { onClick = {
joinFromInviteModalOpen.value = true joinFromInviteModalOpen.value = true
} }
) )
ListItem( SheetButton(
headlineContent = { headlineContent = {
Text(stringResource(id = R.string.add_server_sheet_create_new)) Text(stringResource(id = R.string.add_server_sheet_create_new))
}, },
@ -86,7 +86,7 @@ fun AddServerSheet() {
contentDescription = null contentDescription = null
) )
}, },
modifier = Modifier.clickable { onClick = {
Toast.makeText( Toast.makeText(
context, context,
context.getString( 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 android.widget.Toast
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
@ -20,7 +19,8 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import chat.revolt.R import chat.revolt.R
import chat.revolt.api.RevoltAPI 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 chat.revolt.internals.Platform
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -43,23 +43,20 @@ fun ChannelContextSheet(channelId: String, onHideSheet: suspend () -> Unit) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
Column { SheetButton(
SheetClickable( headlineContent = {
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text( Text(
text = stringResource(id = R.string.channel_context_sheet_actions_copy_id), text = stringResource(id = R.string.channel_context_sheet_actions_copy_id),
style = style
) )
} },
) { leadingContent = {
if (channel.id == null) return@SheetClickable 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)) clipboardManager.setText(AnnotatedString(channel.id))
@ -75,22 +72,21 @@ fun ChannelContextSheet(channelId: String, onHideSheet: suspend () -> Unit) {
onHideSheet() onHideSheet()
} }
} }
SheetClickable(
icon = { modifier ->
Icon(
painter = painterResource(id = R.drawable.ic_eye_check_24dp),
contentDescription = null,
modifier = modifier
) )
},
label = { style -> SheetButton(
headlineContent = {
Text( Text(
text = stringResource(id = R.string.channel_context_sheet_actions_mark_read), text = stringResource(id = R.string.channel_context_sheet_actions_mark_read),
style = style
) )
} },
) { leadingContent = {
Icon(
painter = painterResource(id = R.drawable.ic_eye_check_24dp),
contentDescription = null
)
},
onClick = {
coroutineScope.launch { coroutineScope.launch {
channel.lastMessageID?.let { channel.lastMessageID?.let {
RevoltAPI.unreads.markAsRead(channelId, it, sync = true) RevoltAPI.unreads.markAsRead(channelId, it, sync = true)
@ -98,5 +94,7 @@ fun ChannelContextSheet(channelId: String, onHideSheet: suspend () -> Unit) {
onHideSheet() onHideSheet()
} }
} }
} )
SheetEnd()
} }

View File

@ -1,13 +1,11 @@
package chat.revolt.sheets package chat.revolt.sheets
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.Icons
import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.filled.Add 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.material.icons.filled.Settings
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
@ -40,7 +38,8 @@ import chat.revolt.api.internals.has
import chat.revolt.api.schemas.ChannelType import chat.revolt.api.schemas.ChannelType
import chat.revolt.callbacks.Action import chat.revolt.callbacks.Action
import chat.revolt.callbacks.ActionChannel 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.components.screens.chat.ChannelSheetHeader
import chat.revolt.internals.extensions.rememberChannelPermissions import chat.revolt.internals.extensions.rememberChannelPermissions
import chat.revolt.screens.chat.dialogs.InviteDialog import chat.revolt.screens.chat.dialogs.InviteDialog
@ -98,74 +97,46 @@ fun ChannelInfoSheet(channelId: String, onHideSheet: suspend () -> Unit) {
return return
} }
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
.verticalScroll(rememberScrollState())
) {
val isDM = ChannelUtils.resolveDMPartner(channel) != null
val partner = ChannelUtils val partner = ChannelUtils
.resolveDMPartner(channel) .resolveDMPartner(channel)
?.let { ?.let {
RevoltAPI.userCache[it] 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( ChannelSheetHeader(
channelName = channel.name channelName = channel.name
?: ChannelUtils.resolveDMName(channel) ?: ChannelUtils.resolveDMName(channel)
?: stringResource(id = R.string.unknown), ?: stringResource(id = R.string.unknown),
channelIcon = channel.icon, channelIcon = channel.icon,
channelType = channel.channelType ?: ChannelType.TextChannel, channelType = channel.channelType ?: ChannelType.TextChannel,
channelDescription = channel.description,
dmPartner = partner 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
)
} else {
channel.description
},
modifier = Modifier.padding(bottom = 10.dp)
)
} }
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)
)
when (channel.channelType) { when (channel.channelType) {
ChannelType.TextChannel, ChannelType.VoiceChannel, ChannelType.Group -> { ChannelType.TextChannel, ChannelType.VoiceChannel, ChannelType.Group -> {
SheetClickable( SheetButton(
icon = { modifier -> headlineContent = {
Icon(
imageVector = Icons.AutoMirrored.Default.List,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text( Text(
text = stringResource(id = R.string.channel_info_sheet_options_members), text = stringResource(id = R.string.channel_info_sheet_options_members),
style = style
) )
} },
) { leadingContent = {
Icon(
imageVector = Icons.AutoMirrored.Default.List,
contentDescription = null
)
},
onClick = {
memberListSheetShown = true memberListSheetShown = true
} }
)
} }
else -> {} else -> {}
@ -179,88 +150,77 @@ fun ChannelInfoSheet(channelId: String, onHideSheet: suspend () -> Unit) {
) { ) {
when (channel.channelType) { when (channel.channelType) {
ChannelType.TextChannel, ChannelType.VoiceChannel -> { ChannelType.TextChannel, ChannelType.VoiceChannel -> {
SheetClickable( SheetButton(
icon = { modifier -> headlineContent = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text( Text(
text = stringResource(id = R.string.channel_info_sheet_options_invite), text = stringResource(id = R.string.channel_info_sheet_options_invite),
style = style
) )
} },
) { leadingContent = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = null
)
},
onClick = {
inviteDialogShown = true inviteDialogShown = true
} }
)
} }
ChannelType.Group -> { ChannelType.Group -> {
SheetClickable( SheetButton(
icon = { modifier -> headlineContent = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
modifier = modifier
)
},
label = { style ->
Text( Text(
text = stringResource(id = R.string.channel_info_sheet_options_add), text = stringResource(id = R.string.channel_info_sheet_options_add),
style = style
) )
} },
) { leadingContent = {
} Icon(
imageVector = Icons.Default.Add,
contentDescription = null
)
},
onClick = {}
)
} }
else -> {} else -> {}
} }
} }
SheetClickable( SheetButton(
icon = { modifier -> headlineContent = {
Icon( Text(
imageVector = Icons.Default.Notifications, text = stringResource(id = R.string.channel_info_sheet_options_notifications_manage),
contentDescription = null,
modifier = modifier
) )
}, },
label = { style -> leadingContent = {
Text( Icon(
text = stringResource( imageVector = Icons.Default.Notifications,
id = R.string.channel_info_sheet_options_notifications_manage contentDescription = null
), )
style = style },
onClick = {}
) )
}
) {
}
if ( if (
(permissions has PermissionBit.ManageChannel || permissions has PermissionBit.ManageRole) (permissions has PermissionBit.ManageChannel || permissions has PermissionBit.ManageRole)
&& (channel.channelType != ChannelType.SavedMessages && channel.channelType != ChannelType.DirectMessage) && (channel.channelType != ChannelType.SavedMessages && channel.channelType != ChannelType.DirectMessage)
) { ) {
SheetClickable( SheetButton(
icon = { modifier -> headlineContent = {
Icon( Text(
imageVector = Icons.Default.Settings, text = stringResource(id = R.string.settings),
contentDescription = null,
modifier = modifier
) )
}, },
label = { style -> leadingContent = {
Text( Icon(
text = stringResource( imageVector = Icons.Default.Settings,
id = R.string.settings contentDescription = null
),
style = style
) )
} },
) { onClick = {
scope.launch { scope.launch {
onHideSheet() onHideSheet()
} }
@ -269,8 +229,8 @@ fun ChannelInfoSheet(channelId: String, onHideSheet: suspend () -> Unit) {
ActionChannel.send(Action.TopNavigate("settings/channel/${channel.id}")) 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 package chat.revolt.sheets
import android.widget.Toast 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.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.layout.width
import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -24,7 +21,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext 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.Emoji
import chat.revolt.api.schemas.Server import chat.revolt.api.schemas.Server
import chat.revolt.components.generic.RemoteImage 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 chat.revolt.internals.Platform
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -62,40 +59,31 @@ fun EmoteInfoSheet(id: String, onDismiss: () -> Unit) {
} }
Column( Column(
modifier = Modifier verticalArrangement = Arrangement.spacedBy(16.dp),
.padding(horizontal = 16.dp, vertical = 8.dp) modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 4.dp),
.verticalScroll(rememberScrollState())
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(0.dp))
) { ) {
RemoteImage( RemoteImage(
url = "$REVOLT_FILES/emojis/$id", url = "$REVOLT_FILES/emojis/$id",
description = emoteInfo?.name, description = emoteInfo?.name,
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
modifier = Modifier modifier = Modifier
.padding(16.dp)
.size(32.dp) .size(32.dp)
) )
Column( Spacer(modifier = Modifier.width(16.dp))
modifier = Modifier.padding(
top = 16.dp, Column {
start = 0.dp,
end = 16.dp,
bottom = 16.dp
)
) {
Text( Text(
text = emoteInfo?.name ?: id, text = ":${emoteInfo?.name ?: id}:",
fontWeight = FontWeight.Bold, fontWeight = FontWeight.SemiBold,
letterSpacing = 1.15.sp letterSpacing = 1.15.sp
) )
Spacer(modifier = Modifier.height(4.dp))
Text( Text(
text = if (parentServer != null) { text = if (parentServer != null) {
stringResource( stringResource(
@ -109,23 +97,22 @@ fun EmoteInfoSheet(id: String, onDismiss: () -> Unit) {
} }
} }
Spacer(modifier = Modifier.height(8.dp)) HorizontalDivider()
}
SheetClickable( SheetButton(
icon = { modifier -> headlineContent = {
Icon( Text(
painter = painterResource(id = R.drawable.ic_content_copy_24dp), text = stringResource(id = R.string.copy)
contentDescription = null,
modifier = modifier
) )
}, },
label = { style -> leadingContent = {
Text( Icon(
text = stringResource(id = R.string.copy), painter = painterResource(id = R.drawable.ic_content_copy_24dp),
style = style contentDescription = null
) )
} },
) { onClick = {
coroutineScope.launch { coroutineScope.launch {
clipboardManager.setText(AnnotatedString(":$id:")) clipboardManager.setText(AnnotatedString(":$id:"))
if (Platform.needsShowClipboardNotification()) { if (Platform.needsShowClipboardNotification()) {
@ -138,5 +125,7 @@ fun EmoteInfoSheet(id: String, onDismiss: () -> Unit) {
} }
onDismiss() onDismiss()
} }
} )
SheetEnd()
} }

View File

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

View File

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

View File

@ -1,10 +1,9 @@
package chat.revolt.sheets package chat.revolt.sheets
import android.widget.Toast 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.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.Icons
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -32,7 +32,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource 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.api.routes.channel.react
import chat.revolt.callbacks.UiCallbacks import chat.revolt.callbacks.UiCallbacks
import chat.revolt.components.chat.Message 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.internals.Platform
import chat.revolt.ui.theme.ClearRippleTheme
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -91,24 +92,21 @@ fun MessageContextSheet(
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_content_copy_24dp), painter = painterResource(id = R.drawable.ic_content_copy_24dp),
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.message_context_sheet_actions_copy), text = stringResource(id = R.string.message_context_sheet_actions_copy)
style = style
) )
} },
) { onClick = {
if (message.content.isNullOrEmpty()) { if (message.content.isNullOrEmpty()) {
coroutineScope.launch { coroutineScope.launch {
shareSheetState.hide() shareSheetState.hide()
@ -121,7 +119,7 @@ fun MessageContextSheet(
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
return@SheetClickable return@SheetButton
} }
if (Platform.needsShowClipboardNotification()) { if (Platform.needsShowClipboardNotification()) {
@ -140,24 +138,23 @@ fun MessageContextSheet(
onHideSheet() onHideSheet()
} }
} }
)
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_link_variant_24dp), painter = painterResource(id = R.drawable.ic_link_variant_24dp),
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource( text = stringResource(
id = R.string.message_context_sheet_actions_copy_link id = R.string.message_context_sheet_actions_copy_link
),
style = style
) )
} )
) { },
onClick = {
if (message.content.isNullOrEmpty()) { if (message.content.isNullOrEmpty()) {
Toast.makeText( Toast.makeText(
context, context,
@ -174,7 +171,7 @@ fun MessageContextSheet(
onHideSheet() onHideSheet()
} }
return@SheetClickable return@SheetButton
} }
val server = RevoltAPI.serverCache.values.find { server -> val server = RevoltAPI.serverCache.values.find { server ->
@ -201,25 +198,24 @@ fun MessageContextSheet(
onHideSheet() onHideSheet()
} }
} }
)
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_content_copy_id_24dp), painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource( text = stringResource(
id = R.string.message_context_sheet_actions_copy_id 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))
@ -240,7 +236,10 @@ fun MessageContextSheet(
onHideSheet() onHideSheet()
} }
} }
)
} }
SheetEnd()
} }
} }
@ -312,14 +311,12 @@ fun MessageContextSheet(
Column( Column(
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
Box( CompositionLocalProvider(value = LocalRippleTheme provides ClearRippleTheme) {
modifier = Modifier Column(
.clip(MaterialTheme.shapes.medium) verticalArrangement = Arrangement.spacedBy(16.dp),
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(0.dp)) modifier = Modifier.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 4.dp),
.padding(bottom = 8.dp)
) { ) {
Message( Message(
message = message.copy( message = message.copy(
@ -327,87 +324,83 @@ fun MessageContextSheet(
masquerade = null masquerade = null
) )
) )
HorizontalDivider()
}
} }
Spacer(modifier = Modifier.height(8.dp)) SheetButton(
leadingContent = {
SheetClickable(
icon = { modifier ->
Icon( Icon(
painter = painterResource(id = R.drawable.ic_reply_24dp), painter = painterResource(id = R.drawable.ic_reply_24dp),
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.message_context_sheet_actions_reply), text = stringResource(id = R.string.message_context_sheet_actions_reply),
style = style
) )
} },
) { onClick = {
coroutineScope.launch { coroutineScope.launch {
UiCallbacks.replyToMessage(messageId) UiCallbacks.replyToMessage(messageId)
onHideSheet() onHideSheet()
} }
} }
)
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_hamburger_plus_24dp), painter = painterResource(id = R.drawable.ic_hamburger_plus_24dp),
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.message_context_sheet_actions_react), 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 -> onClick = {
showReactSheet = true
}
)
if (message.author == RevoltAPI.selfId) {
SheetButton(
leadingContent = {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = null
)
},
headlineContent = {
Text( Text(
text = stringResource(id = R.string.message_context_sheet_actions_edit), text = stringResource(id = R.string.message_context_sheet_actions_edit),
style = style
) )
} },
) { onClick = {
coroutineScope.launch { coroutineScope.launch {
UiCallbacks.editMessage(messageId) UiCallbacks.editMessage(messageId)
onHideSheet() onHideSheet()
} }
} }
)
} }
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_eye_off_24dp), painter = painterResource(id = R.drawable.ic_eye_off_24dp),
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.message_context_sheet_actions_mark_unread), text = stringResource(id = R.string.message_context_sheet_actions_mark_unread),
style = style
) )
} },
) { onClick = {
Toast.makeText( Toast.makeText(
context, context,
context.getString(R.string.comingsoon_toast), context.getString(R.string.comingsoon_toast),
@ -418,24 +411,24 @@ fun MessageContextSheet(
onHideSheet() onHideSheet()
} }
} }
)
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_share_24dp), painter = painterResource(id = R.drawable.ic_share_24dp),
contentDescription = null, contentDescription = null,
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.share), text = stringResource(id = R.string.share),
style = style
) )
} },
) { onClick = {
showShareSheet = true showShareSheet = true
} }
)
if ( if (
(message.channel?.let { (message.channel?.let {
@ -446,47 +439,47 @@ fun MessageContextSheet(
) )
} ?: 0) has PermissionBit.ManageMessages || message.author == RevoltAPI.selfId } ?: 0) has PermissionBit.ManageMessages || message.author == RevoltAPI.selfId
) { ) {
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
imageVector = Icons.Default.Delete, imageVector = Icons.Default.Delete,
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.message_context_sheet_actions_delete), text = stringResource(id = R.string.message_context_sheet_actions_delete),
style = style
) )
}, },
dangerous = true dangerous = true,
) { onClick = {
showDeleteMessageConfirmation = true showDeleteMessageConfirmation = true
} }
)
} }
if (message.author != RevoltAPI.selfId) { if (message.author != RevoltAPI.selfId) {
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
painter = painterResource(id = R.drawable.ic_flag_24dp), painter = painterResource(id = R.drawable.ic_flag_24dp),
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.message_context_sheet_actions_report), text = stringResource(id = R.string.message_context_sheet_actions_report),
style = style
) )
}, },
dangerous = true dangerous = true,
) { onClick = {
coroutineScope.launch { coroutineScope.launch {
onReportMessage() onReportMessage()
} }
} },
} )
}
SheetEnd()
} }
} }

View File

@ -1,14 +1,16 @@
package chat.revolt.sheets package chat.revolt.sheets
import androidx.compose.foundation.ExperimentalFoundationApi 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.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
@ -16,7 +18,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -26,7 +27,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.PlatformTextStyle 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.api.schemas.User
import chat.revolt.components.chat.MemberListItem import chat.revolt.components.chat.MemberListItem
import chat.revolt.components.generic.RemoteImage import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.SheetEnd
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@ -118,13 +119,18 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
if (reactionEmoji?.isNotEmpty() == true) { if (reactionEmoji?.isNotEmpty() == true) {
item("info") { item("info") {
val current = reactionEmoji[selectedReactionIndex] val current = reactionEmoji[selectedReactionIndex]
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(
top = 16.dp,
start = 16.dp,
end = 16.dp,
bottom = 4.dp
),
) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically
modifier = Modifier
.padding(16.dp)
.clip(MaterialTheme.shapes.medium)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(0.dp))
) { ) {
if (current.isUlid()) { if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current } val cached = extendedEmojiInfo.find { it.id == current }
@ -133,13 +139,11 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
description = cached?.name, description = cached?.name,
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
modifier = Modifier modifier = Modifier
.padding(16.dp)
.size(32.dp) .size(32.dp)
) )
} else { } else {
Box( Box(
modifier = Modifier modifier = Modifier
.padding(16.dp)
.size(32.dp), .size(32.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
@ -159,30 +163,27 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
} }
} }
Column( Spacer(modifier = Modifier.width(16.dp))
modifier = Modifier.padding(
top = 16.dp, Column {
start = 0.dp,
end = 16.dp,
bottom = 16.dp
)
) {
if (current.isUlid()) { if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current } val cached = extendedEmojiInfo.find { it.id == current }
Text( Text(
text = ":${cached?.name ?: current}:", text = ":${cached?.name ?: current}:",
fontWeight = FontWeight.Bold, fontWeight = FontWeight.SemiBold,
letterSpacing = 1.15.sp letterSpacing = 1.15.sp
) )
} else { } else {
Text( Text(
text = MessageProcessor.emoji.unicodeAsShortcode(current) text = MessageProcessor.emoji.unicodeAsShortcode(current)
?: current, ?: current,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.SemiBold,
letterSpacing = 1.15.sp letterSpacing = 1.15.sp
) )
} }
Spacer(modifier = Modifier.height(4.dp))
Text( Text(
text = if (current.isUlid()) { text = if (current.isUlid()) {
val cached = extendedEmojiInfo.find { it.id == current } val cached = extendedEmojiInfo.find { it.id == current }
@ -207,6 +208,9 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
) )
} }
} }
HorizontalDivider()
}
} }
val reactionsForEmoji = reactions[reactionEmoji[selectedReactionIndex]] val reactionsForEmoji = reactions[reactionEmoji[selectedReactionIndex]]
@ -236,7 +240,7 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
} }
item("bottom") { item("bottom") {
Spacer(Modifier.size(16.dp)) SheetEnd()
} }
} }
} }

View File

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

View File

@ -19,7 +19,8 @@ import androidx.compose.ui.unit.dp
import chat.revolt.R import chat.revolt.R
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.routes.user.patchSelf 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.asApiName
import chat.revolt.components.generic.presenceFromStatus import chat.revolt.components.generic.presenceFromStatus
import chat.revolt.components.screens.settings.UserOverview import chat.revolt.components.screens.settings.UserOverview
@ -52,24 +53,25 @@ fun StatusSheet(onBeforeNavigation: () -> Unit, onGoSettings: () -> Unit) {
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
}
SheetClickable( SheetButton(
icon = { modifier -> leadingContent = {
Icon( Icon(
imageVector = Icons.Default.Settings, imageVector = Icons.Default.Settings,
contentDescription = null, contentDescription = null
modifier = modifier
) )
}, },
label = { style -> headlineContent = {
Text( Text(
text = stringResource(id = R.string.settings), text = stringResource(id = R.string.settings)
style = style
) )
} },
) { onClick = {
onBeforeNavigation() onBeforeNavigation()
onGoSettings() 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_copy_id_copied">Copied channel ID to clipboard</string>
<string name="channel_context_sheet_actions_mark_read">Mark as read</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_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">Copy ID</string>
<string name="server_context_sheet_actions_copy_id_copied">Copied server ID to clipboard</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="report_block_no">Don\'t block</string>
<string name="link_type_no_intent">No app found to handle this link</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_message">You\'ve been invited to join this server. Would you like to join?</string>
<string name="invite_join">Join</string> <string name="invite_join">Join</string>