feat: server context sheet

Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
Infi 2023-08-09 18:36:56 +02:00
parent 0e67637ddf
commit 04237e3dfd
5 changed files with 330 additions and 3 deletions

View File

@ -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)
}
}

View File

@ -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
},
)
}
}

View File

@ -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
}
}
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H19A2,2 0 0,1 21,5M7,12L12,17V14H16V10H12V7L7,12Z" />
</vector>

View File

@ -202,9 +202,19 @@
<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>
<string name="server_context_sheet_actions_mark_read">Mark as read</string>
<string name="server_context_sheet_actions_leave">Leave</string>
<string name="server_context_sheet_actions_leave_confirm">Leave %1$s?</string>
<string name="server_context_sheet_actions_leave_confirm_eyebrow">Until you get invited back, you won\'t be able to rejoin.</string>
<string name="server_context_sheet_actions_leave_confirm_yes">Leave</string>
<string name="server_context_sheet_actions_leave_confirm_no">Stay</string>
<string name="server_context_sheet_actions_leave_silently">Leave Silently</string>
<string name="user_context_sheet_category_bio">Bio</string>
<string name="user_context_sheet_bio_empty">This user hasn\'t set a bio yet.</string>