diff --git a/app/src/main/java/chat/revolt/api/routes/server/Server.kt b/app/src/main/java/chat/revolt/api/routes/server/Server.kt
index 0938a454..5c4fa396 100644
--- a/app/src/main/java/chat/revolt/api/routes/server/Server.kt
+++ b/app/src/main/java/chat/revolt/api/routes/server/Server.kt
@@ -6,6 +6,7 @@ import chat.revolt.api.RevoltHttp
import chat.revolt.api.RevoltJson
import chat.revolt.api.schemas.Member
import chat.revolt.api.schemas.User
+import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.client.request.put
@@ -86,4 +87,12 @@ suspend fun fetchMember(serverId: String, userId: String, pure: Boolean = false)
}
return member
+}
+
+suspend fun leaveOrDeleteServer(serverId: String, leaveSilently: Boolean = false) {
+ RevoltHttp.delete("/servers/$serverId") {
+ headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken)
+
+ parameter("leave_silently", leaveSilently)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt
index f800a308..270ab4f3 100644
--- a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt
+++ b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt
@@ -79,6 +79,7 @@ import chat.revolt.screens.chat.views.HomeScreen
import chat.revolt.screens.chat.views.NoCurrentChannelScreen
import chat.revolt.screens.chat.views.channel.ChannelScreen
import chat.revolt.sheets.AddServerSheet
+import chat.revolt.sheets.ServerContextSheet
import chat.revolt.sheets.StatusSheet
import chat.revolt.sheets.UserContextSheet
import com.airbnb.lottie.RenderMode
@@ -392,9 +393,13 @@ fun ChatRouterScreen(
showServerContextSheet = false
},
) {
- Column {
- Text(text = "this is server context sheet for $serverContextSheetTarget")
- }
+ ServerContextSheet(
+ serverId = serverContextSheetTarget,
+ onHideSheet = {
+ serverContextSheetState.hide()
+ showServerContextSheet = false
+ },
+ )
}
}
diff --git a/app/src/main/java/chat/revolt/sheets/ServerContextSheet.kt b/app/src/main/java/chat/revolt/sheets/ServerContextSheet.kt
new file mode 100644
index 00000000..75e91e6b
--- /dev/null
+++ b/app/src/main/java/chat/revolt/sheets/ServerContextSheet.kt
@@ -0,0 +1,294 @@
+package chat.revolt.sheets
+
+import android.widget.Toast
+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.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.CircularProgressIndicator
+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
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+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.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import chat.revolt.R
+import chat.revolt.api.REVOLT_FILES
+import chat.revolt.api.RevoltAPI
+import chat.revolt.api.routes.server.leaveOrDeleteServer
+import chat.revolt.components.generic.IconPlaceholder
+import chat.revolt.components.generic.RemoteImage
+import chat.revolt.components.generic.SheetClickable
+import chat.revolt.components.generic.UIMarkdown
+import kotlinx.coroutines.launch
+
+@Composable
+fun ServerContextSheet(
+ serverId: String,
+ onHideSheet: suspend () -> Unit
+) {
+ val server = RevoltAPI.serverCache[serverId]
+
+ if (server == null) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(200.dp)
+ ) {
+ CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
+ }
+ return
+ }
+
+ val coroutineScope = rememberCoroutineScope()
+ val clipboardManager = LocalClipboardManager.current
+ val context = LocalContext.current
+
+ var showLeaveConfirmation by remember { mutableStateOf(false) }
+ var leaveSilently by remember { mutableStateOf(false) }
+
+ if (showLeaveConfirmation) {
+ AlertDialog(onDismissRequest = {
+ showLeaveConfirmation = false
+ },
+ title = {
+ Text(
+ text = stringResource(
+ id = R.string.server_context_sheet_actions_leave_confirm,
+ server.name ?: stringResource(R.string.unknown)
+ )
+ )
+ },
+ text = {
+ Column {
+ Text(text = stringResource(id = R.string.server_context_sheet_actions_leave_confirm_eyebrow))
+ Row(
+ Modifier
+ .fillMaxWidth()
+ .padding(start = 0.dp, end = 0.dp, top = 16.dp, bottom = 0.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Checkbox(
+ checked = leaveSilently,
+ onCheckedChange = { leaveSilently = it },
+ )
+ Text(
+ text = stringResource(id = R.string.server_context_sheet_actions_leave_silently),
+ modifier = Modifier.padding(start = 4.dp)
+ )
+ }
+ }
+ },
+ confirmButton = {
+ Button(
+ onClick = {
+ coroutineScope.launch {
+ onHideSheet()
+ }
+ coroutineScope.launch {
+ leaveOrDeleteServer(serverId, leaveSilently)
+ }
+ }
+ ) {
+ Text(text = stringResource(id = R.string.server_context_sheet_actions_leave_confirm_yes))
+ }
+ },
+ dismissButton = {
+ TextButton(
+ onClick = {
+ showLeaveConfirmation = false
+ }
+ ) {
+ Text(text = stringResource(id = R.string.server_context_sheet_actions_leave_confirm_no))
+ }
+ }
+ )
+ }
+
+ Column(
+ modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 0.dp, bottom = 16.dp)
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(MaterialTheme.shapes.large),
+ contentAlignment = Alignment.BottomStart
+ ) {
+ server.banner?.let {
+ Box(
+ modifier = Modifier
+ .background(Color.Black.copy(alpha = 0.25f))
+ .height(166.dp)
+ .fillMaxWidth()
+ )
+
+ RemoteImage(
+ url = "$REVOLT_FILES/banners/${it.id}",
+ description = null,
+ modifier = Modifier
+ .height(166.dp)
+ .fillMaxWidth(),
+ contentScale = ContentScale.FillWidth
+ )
+
+ Box(
+ modifier = Modifier
+ .background(
+ Brush.verticalGradient(
+ listOf(
+ Color.Transparent,
+ Color.Black.copy(alpha = 0.7f)
+ )
+ )
+ )
+ .height(166.dp)
+ .fillMaxWidth()
+ )
+ }
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(16.dp)
+ ) {
+ server.icon?.let {
+ RemoteImage(
+ url = "$REVOLT_FILES/icons/${it.id}/server.png?max_side=256",
+ description = null,
+ modifier = Modifier
+ .clip(CircleShape)
+ .height(48.dp)
+ .width(48.dp),
+ contentScale = ContentScale.Crop
+ )
+ } ?: run {
+ IconPlaceholder(
+ name = server.name ?: stringResource(R.string.unknown),
+ modifier = Modifier
+ .clip(CircleShape)
+ .height(48.dp)
+ .width(48.dp)
+ )
+ }
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ Text(
+ text = server.name ?: stringResource(R.string.unknown),
+ style = MaterialTheme.typography.labelLarge,
+ fontSize = 16.sp
+ )
+ }
+ }
+
+ 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)
+ )
+
+ UIMarkdown(
+ text = if (server.description?.isBlank() == false) server.description else stringResource(
+ R.string.server_context_sheet_description_empty
+ ),
+ )
+
+ 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 = {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_content_copy_id_24dp),
+ contentDescription = null,
+ modifier = it
+ )
+ }, label = {
+ Text(
+ text = stringResource(id = R.string.server_context_sheet_actions_copy_id),
+ style = it
+ )
+ }) {
+ if (server.id == null) return@SheetClickable
+
+ clipboardManager.setText(AnnotatedString(server.id))
+ Toast.makeText(
+ context,
+ context.getString(R.string.server_context_sheet_actions_copy_id_copied),
+ Toast.LENGTH_SHORT
+ ).show()
+
+ coroutineScope.launch {
+ onHideSheet()
+ }
+ }
+
+ SheetClickable(icon = {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_eye_check_24dp),
+ contentDescription = null,
+ modifier = it
+ )
+ }, label = {
+ Text(
+ text = stringResource(id = R.string.server_context_sheet_actions_mark_read),
+ style = it
+ )
+ }) {
+ coroutineScope.launch {
+ server.id?.let {
+ RevoltAPI.unreads.markServerAsRead(it, sync = true)
+ }
+ onHideSheet()
+ }
+ }
+
+ if (server.owner != RevoltAPI.selfId) {
+ SheetClickable(icon = {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_arrow_left_bold_box_24dp),
+ contentDescription = null,
+ modifier = it
+ )
+ }, label = {
+ Text(
+ text = stringResource(id = R.string.server_context_sheet_actions_leave),
+ style = it
+ )
+ }, dangerous = true) {
+ showLeaveConfirmation = true
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_arrow_left_bold_box_24dp.xml b/app/src/main/res/drawable/ic_arrow_left_bold_box_24dp.xml
new file mode 100644
index 00000000..bbaee11d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_left_bold_box_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 67efa1c7..8c5a23cc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -202,9 +202,19 @@
Copied channel ID to clipboard
Mark as read
+ Description
+ There hasn\'t been a description set for this server yet.
+ Actions
+
Copy ID
Copied server ID to clipboard
Mark as read
+ Leave
+ Leave %1$s?
+ Until you get invited back, you won\'t be able to rejoin.
+ Leave
+ Stay
+ Leave Silently
Bio
This user hasn\'t set a bio yet.