feat: remove members from group DMs
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
8b276aade3
commit
a5b240f5fa
|
|
@ -6,3 +6,5 @@
|
|||
/other.xml
|
||||
# GitHub Copilot persisted chat sessions
|
||||
/copilot/chatSessions
|
||||
# User-specific files
|
||||
/deploymentTargetSelector.xml
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -5,11 +5,14 @@ import chat.revolt.api.RevoltHttp
|
|||
import chat.revolt.api.RevoltJson
|
||||
import chat.revolt.api.schemas.Channel
|
||||
import chat.revolt.screens.create.MAX_ADDABLE_PEOPLE_IN_GROUP
|
||||
import io.ktor.client.request.delete
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.put
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.contentType
|
||||
import io.ktor.http.isSuccess
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
|
||||
|
|
@ -37,4 +40,20 @@ suspend fun createGroupDM(name: String, members: List<String>): Channel {
|
|||
}
|
||||
|
||||
return RevoltJson.decodeFromString(Channel.serializer(), response)
|
||||
}
|
||||
|
||||
suspend fun removeMember(channelId: String, userId: String) {
|
||||
val response = RevoltHttp.delete("/channels/$channelId/recipients/$userId")
|
||||
|
||||
if (!response.status.isSuccess()) {
|
||||
throw Error(response.status.toString())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun addMember(channelId: String, userId: String) {
|
||||
val response = RevoltHttp.put("/channels/$channelId/recipients/$userId")
|
||||
|
||||
if (!response.status.isSuccess()) {
|
||||
throw Error(response.status.toString())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
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
|
||||
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
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
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.internals.Platform
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun ColumnScope.GroupDMMemberContextSheet(
|
||||
userId: String,
|
||||
channelId: String,
|
||||
dismissSheet: suspend () -> Unit,
|
||||
onRequestUpdateMembers: suspend () -> Unit
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val channel = RevoltAPI.channelCache[channelId]
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(channel) {
|
||||
if (channel == null) {
|
||||
dismissSheet()
|
||||
}
|
||||
}
|
||||
|
||||
if (channel == null) return
|
||||
|
||||
if (channel.owner == RevoltAPI.selfId && userId != RevoltAPI.selfId) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
CompositionLocalProvider(value = LocalContentColor provides MaterialTheme.colorScheme.error) {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.member_context_sheet_remove_from_channel,
|
||||
channel.name ?: stringResource(R.string.unknown)
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
CompositionLocalProvider(value = LocalContentColor provides MaterialTheme.colorScheme.error) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_account_cancel_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
removeMember(channelId, userId)
|
||||
onRequestUpdateMembers()
|
||||
dismissSheet()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// TODO replace with something useful (currently so that your sheet is not empty if you don't have permissions)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.user_info_sheet_copy_id))
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_content_copy_id_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable {
|
||||
clipboardManager.setText(AnnotatedString(userId))
|
||||
|
||||
if (Platform.needsShowClipboardNotification()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.copied),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ColumnScope.ServerMemberContextSheet(
|
||||
userId: String,
|
||||
serverId: String,
|
||||
channelId: String,
|
||||
dismissSheet: suspend () -> Unit,
|
||||
onRequestUpdateMembers: suspend () -> Unit
|
||||
) {
|
||||
val server = RevoltAPI.serverCache[serverId]
|
||||
val channel = RevoltAPI.channelCache[channelId]
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(server) {
|
||||
if (server == null || channel == null) {
|
||||
dismissSheet()
|
||||
}
|
||||
}
|
||||
|
||||
if (server == null || channel == null) return
|
||||
|
||||
// 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(
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.user_info_sheet_copy_id))
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_content_copy_id_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable {
|
||||
clipboardManager.setText(AnnotatedString(userId))
|
||||
|
||||
if (Platform.needsShowClipboardNotification()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.copied),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import android.annotation.SuppressLint
|
|||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -209,8 +209,10 @@ fun MemberListSheet(
|
|||
serverId: String? = null,
|
||||
viewModel: MemberListSheetViewModel = hiltViewModel()
|
||||
) {
|
||||
var showUserContextSheet by remember { mutableStateOf(false) }
|
||||
var userContextSheetTarget by remember { mutableStateOf("") }
|
||||
var showUserInfoSheet by remember { mutableStateOf(false) }
|
||||
var userInfoSheetTarget by remember { mutableStateOf("") }
|
||||
var showMemberContextSheet by remember { mutableStateOf(false) }
|
||||
var memberContextSheetTarget by remember { mutableStateOf("") }
|
||||
|
||||
// We use LaunchedEffect to make sure that this is called every time any of the users status changes
|
||||
LaunchedEffect(RevoltAPI.userCache) {
|
||||
|
|
@ -223,26 +225,64 @@ fun MemberListSheet(
|
|||
}
|
||||
}
|
||||
|
||||
if (showUserContextSheet) {
|
||||
if (showUserInfoSheet) {
|
||||
val userContextSheetState = rememberModalBottomSheetState()
|
||||
|
||||
ModalBottomSheet(
|
||||
sheetState = userContextSheetState,
|
||||
onDismissRequest = {
|
||||
showUserContextSheet = false
|
||||
showUserInfoSheet = false
|
||||
}
|
||||
) {
|
||||
UserInfoSheet(
|
||||
userId = userContextSheetTarget,
|
||||
userId = userInfoSheetTarget,
|
||||
serverId = serverId,
|
||||
dismissSheet = {
|
||||
userContextSheetState.hide()
|
||||
showUserContextSheet = false
|
||||
showUserInfoSheet = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (showMemberContextSheet) {
|
||||
val memberContextSheetState = rememberModalBottomSheetState()
|
||||
|
||||
ModalBottomSheet(
|
||||
sheetState = memberContextSheetState,
|
||||
onDismissRequest = {
|
||||
showMemberContextSheet = false
|
||||
}
|
||||
) {
|
||||
if (serverId != null) {
|
||||
ServerMemberContextSheet(
|
||||
userId = memberContextSheetTarget,
|
||||
serverId = serverId,
|
||||
channelId = channelId,
|
||||
onRequestUpdateMembers = {
|
||||
viewModel.fetchServerMemberList(serverId, channelId)
|
||||
},
|
||||
dismissSheet = {
|
||||
memberContextSheetState.hide()
|
||||
showMemberContextSheet = false
|
||||
}
|
||||
)
|
||||
} else {
|
||||
GroupDMMemberContextSheet(
|
||||
userId = memberContextSheetTarget,
|
||||
channelId = channelId,
|
||||
onRequestUpdateMembers = {
|
||||
viewModel.fetchGroupMemberList(channelId)
|
||||
},
|
||||
dismissSheet = {
|
||||
memberContextSheetState.hide()
|
||||
showMemberContextSheet = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (viewModel.fullItemList.isEmpty()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -281,10 +321,19 @@ fun MemberListSheet(
|
|||
member = item.member,
|
||||
serverId = serverId,
|
||||
userId = item.member.id.user,
|
||||
modifier = Modifier.clickable {
|
||||
userContextSheetTarget = item.member.id.user
|
||||
showUserContextSheet = true
|
||||
}
|
||||
modifier = Modifier
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
userInfoSheetTarget = item.member.id.user
|
||||
showUserInfoSheet = true
|
||||
},
|
||||
onClickLabel = stringResource(R.string.user_info_sheet_open),
|
||||
onLongClick = {
|
||||
memberContextSheetTarget = item.member.id.user
|
||||
showMemberContextSheet = true
|
||||
},
|
||||
onLongClickLabel = stringResource(R.string.member_context_sheet_open)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -294,10 +343,18 @@ fun MemberListSheet(
|
|||
member = null,
|
||||
serverId = serverId,
|
||||
userId = item.user.id,
|
||||
modifier = Modifier.clickable {
|
||||
userContextSheetTarget = item.user.id
|
||||
showUserContextSheet = true
|
||||
}
|
||||
modifier = Modifier.combinedClickable(
|
||||
onClick = {
|
||||
userInfoSheetTarget = item.user.id
|
||||
showUserInfoSheet = true
|
||||
},
|
||||
onClickLabel = stringResource(R.string.user_info_sheet_open),
|
||||
onLongClick = {
|
||||
memberContextSheetTarget = item.user.id
|
||||
showMemberContextSheet = true
|
||||
},
|
||||
onLongClickLabel = stringResource(R.string.member_context_sheet_open)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,6 +304,7 @@
|
|||
<string name="server_context_sheet_actions_leave_silently">Leave Silently</string>
|
||||
<string name="server_context_sheet_actions_report">Report</string>
|
||||
|
||||
<string name="user_info_sheet_open">Open user info</string>
|
||||
<string name="user_info_sheet_user_not_found">Can\'t resolve this user</string>
|
||||
<string name="user_info_sheet_user_not_found_description">This user may have been deleted or you may not have permission to view them.</string>
|
||||
<string name="user_info_sheet_category_bio">Bio</string>
|
||||
|
|
@ -330,6 +331,9 @@
|
|||
<string name="user_info_sheet_user_is_bot">This is a bot.</string>
|
||||
<string name="user_info_sheet_user_is_bot_easter_egg">This is a bot. It has a plan.</string>
|
||||
|
||||
<string name="member_context_sheet_open">Open member options</string>
|
||||
<string name="member_context_sheet_remove_from_channel">Remove from %1$s</string>
|
||||
|
||||
<string name="user_badge_developer">Developer</string>
|
||||
<string name="user_badge_translator">Translator</string>
|
||||
<string name="user_badge_supporter">Supporter</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue