feat: make report dialogues up to date, add user report
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
f611296670
commit
9da6b040a8
|
|
@ -12,8 +12,16 @@ import kotlinx.serialization.encoding.Encoder
|
||||||
enum class ContentReportReason(val value: String) {
|
enum class ContentReportReason(val value: String) {
|
||||||
NoneSpecified("NoneSpecified"),
|
NoneSpecified("NoneSpecified"),
|
||||||
Illegal("Illegal"),
|
Illegal("Illegal"),
|
||||||
|
IllegalGoods("IllegalGoods"),
|
||||||
|
IllegalExtortion("IllegalExtortion"),
|
||||||
|
IllegalPornography("IllegalPornography"),
|
||||||
|
IllegalHacking("IllegalHacking"),
|
||||||
|
ExtremeViolence("ExtremeViolence"),
|
||||||
PromotesHarm("PromotesHarm"),
|
PromotesHarm("PromotesHarm"),
|
||||||
|
UnsolicitedSpam("UnsolicitedSpam"),
|
||||||
|
Raid("Raid"),
|
||||||
SpamAbuse("SpamAbuse"),
|
SpamAbuse("SpamAbuse"),
|
||||||
|
ScamsFraud("ScamsFraud"),
|
||||||
Malware("Malware"),
|
Malware("Malware"),
|
||||||
Harassment("Harassment");
|
Harassment("Harassment");
|
||||||
|
|
||||||
|
|
@ -30,8 +38,16 @@ enum class ContentReportReason(val value: String) {
|
||||||
when (val value = decoder.decodeString()) {
|
when (val value = decoder.decodeString()) {
|
||||||
"NoneSpecified" -> NoneSpecified
|
"NoneSpecified" -> NoneSpecified
|
||||||
"Illegal" -> Illegal
|
"Illegal" -> Illegal
|
||||||
|
"IllegalGoods" -> IllegalGoods
|
||||||
|
"IllegalExtortion" -> IllegalExtortion
|
||||||
|
"IllegalPornography" -> IllegalPornography
|
||||||
|
"IllegalHacking" -> IllegalHacking
|
||||||
|
"ExtremeViolence" -> ExtremeViolence
|
||||||
"PromotesHarm" -> PromotesHarm
|
"PromotesHarm" -> PromotesHarm
|
||||||
|
"UnsolicitedSpam" -> UnsolicitedSpam
|
||||||
|
"Raid" -> Raid
|
||||||
"SpamAbuse" -> SpamAbuse
|
"SpamAbuse" -> SpamAbuse
|
||||||
|
"ScamsFraud" -> ScamsFraud
|
||||||
"Malware" -> Malware
|
"Malware" -> Malware
|
||||||
"Harassment" -> Harassment
|
"Harassment" -> Harassment
|
||||||
else -> throw IllegalArgumentException("Unknown ContentReportReason: $value")
|
else -> throw IllegalArgumentException("Unknown ContentReportReason: $value")
|
||||||
|
|
@ -46,6 +62,7 @@ enum class ContentReportReason(val value: String) {
|
||||||
@Serializable
|
@Serializable
|
||||||
enum class UserReportReason(val value: String) {
|
enum class UserReportReason(val value: String) {
|
||||||
NoneSpecified("NoneSpecified"),
|
NoneSpecified("NoneSpecified"),
|
||||||
|
UnsolicitedSpam("UnsolicitedSpam"),
|
||||||
SpamAbuse("SpamAbuse"),
|
SpamAbuse("SpamAbuse"),
|
||||||
InappropriateProfile("InappropriateProfile"),
|
InappropriateProfile("InappropriateProfile"),
|
||||||
Impersonation("Impersonation"),
|
Impersonation("Impersonation"),
|
||||||
|
|
@ -64,6 +81,7 @@ enum class UserReportReason(val value: String) {
|
||||||
override fun deserialize(decoder: Decoder): UserReportReason =
|
override fun deserialize(decoder: Decoder): UserReportReason =
|
||||||
when (val value = decoder.decodeString()) {
|
when (val value = decoder.decodeString()) {
|
||||||
"NoneSpecified" -> NoneSpecified
|
"NoneSpecified" -> NoneSpecified
|
||||||
|
"UnsolicitedSpam" -> UnsolicitedSpam
|
||||||
"SpamAbuse" -> SpamAbuse
|
"SpamAbuse" -> SpamAbuse
|
||||||
"InappropriateProfile" -> InappropriateProfile
|
"InappropriateProfile" -> InappropriateProfile
|
||||||
"Impersonation" -> Impersonation
|
"Impersonation" -> Impersonation
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ fun SelfUserOverview() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UserOverview(user: User) {
|
fun UserOverview(user: User, internalPadding: Boolean = true) {
|
||||||
var profile by remember { mutableStateOf<Profile?>(null) }
|
var profile by remember { mutableStateOf<Profile?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(user) {
|
LaunchedEffect(user) {
|
||||||
|
|
@ -67,7 +67,7 @@ fun UserOverview(user: User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RawUserOverview(user, profile)
|
RawUserOverview(user, profile, internalPadding = internalPadding)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -75,7 +75,8 @@ fun RawUserOverview(
|
||||||
user: User,
|
user: User,
|
||||||
profile: Profile? = null,
|
profile: Profile? = null,
|
||||||
pfpUrl: String? = null,
|
pfpUrl: String? = null,
|
||||||
backgroundUrl: String? = null
|
backgroundUrl: String? = null,
|
||||||
|
internalPadding: Boolean = true
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var teamMemberFlair by remember { mutableStateOf<Brush?>(null) }
|
var teamMemberFlair by remember { mutableStateOf<Brush?>(null) }
|
||||||
|
|
@ -94,7 +95,7 @@ fun RawUserOverview(
|
||||||
Box(
|
Box(
|
||||||
contentAlignment = Alignment.BottomStart,
|
contentAlignment = Alignment.BottomStart,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = if (internalPadding) 16.dp else 0.dp)
|
||||||
.clip(MaterialTheme.shapes.large)
|
.clip(MaterialTheme.shapes.large)
|
||||||
.then(
|
.then(
|
||||||
if (user.id in SpecialUsers.TEAM_MEMBER_FLAIRS.keys) {
|
if (user.id in SpecialUsers.TEAM_MEMBER_FLAIRS.keys) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.DismissibleDrawerSheet
|
import androidx.compose.material3.DismissibleDrawerSheet
|
||||||
import androidx.compose.material3.DismissibleNavigationDrawer
|
import androidx.compose.material3.DismissibleNavigationDrawer
|
||||||
import androidx.compose.material3.DrawerState
|
import androidx.compose.material3.DrawerState
|
||||||
|
|
@ -95,6 +94,7 @@ import chat.revolt.internals.Changelogs
|
||||||
import chat.revolt.ndk.Pipebomb
|
import chat.revolt.ndk.Pipebomb
|
||||||
import chat.revolt.persistence.KVStorage
|
import chat.revolt.persistence.KVStorage
|
||||||
import chat.revolt.screens.chat.dialogs.safety.ReportMessageDialog
|
import chat.revolt.screens.chat.dialogs.safety.ReportMessageDialog
|
||||||
|
import chat.revolt.screens.chat.dialogs.safety.ReportUserDialog
|
||||||
import chat.revolt.screens.chat.views.FriendsScreen
|
import chat.revolt.screens.chat.views.FriendsScreen
|
||||||
import chat.revolt.screens.chat.views.HomeScreen
|
import chat.revolt.screens.chat.views.HomeScreen
|
||||||
import chat.revolt.screens.chat.views.NoCurrentChannelScreen
|
import chat.revolt.screens.chat.views.NoCurrentChannelScreen
|
||||||
|
|
@ -1050,16 +1050,10 @@ fun ChannelNavigator(
|
||||||
dialog("report/user/{userId}") { backStackEntry ->
|
dialog("report/user/{userId}") { backStackEntry ->
|
||||||
val userId = backStackEntry.arguments?.getString("userId")
|
val userId = backStackEntry.arguments?.getString("userId")
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
AlertDialog(onDismissRequest = {
|
ReportUserDialog(
|
||||||
navController.popBackStack()
|
navController = navController,
|
||||||
}) {
|
userId = userId
|
||||||
Text("Report user $userId")
|
)
|
||||||
Button(onClick = {
|
|
||||||
navController.popBackStack()
|
|
||||||
}) {
|
|
||||||
Text("Close")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconToggleButton
|
import androidx.compose.material3.IconToggleButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -50,13 +53,14 @@ import chat.revolt.components.chat.Message
|
||||||
import chat.revolt.components.generic.FormTextField
|
import chat.revolt.components.generic.FormTextField
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
enum class ReportFlowState {
|
enum class MessageReportFlowState {
|
||||||
Reason,
|
Reason,
|
||||||
Sending,
|
Sending,
|
||||||
Done,
|
Done,
|
||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ReportMessageDialog(navController: NavController, messageId: String) {
|
fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
val message = RevoltAPI.messageCache[messageId]
|
val message = RevoltAPI.messageCache[messageId]
|
||||||
|
|
@ -68,20 +72,28 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
val author = RevoltAPI.userCache[message.author]
|
val author = RevoltAPI.userCache[message.author]
|
||||||
val messageIsBridged = author?.let { author.bot != null && message.masquerade != null } ?: false
|
val messageIsBridged = author?.let { author.bot != null && message.masquerade != null } ?: false
|
||||||
|
|
||||||
val state = remember { mutableStateOf(ReportFlowState.Reason) }
|
val state = remember { mutableStateOf(MessageReportFlowState.Reason) }
|
||||||
|
|
||||||
val selectedReason = remember { mutableStateOf("Illegal") }
|
val selectedReason = remember { mutableStateOf("Illegal") }
|
||||||
val userAddedContext = remember { mutableStateOf("") }
|
val userAddedContext = remember { mutableStateOf("") }
|
||||||
|
|
||||||
when (state.value) {
|
when (state.value) {
|
||||||
ReportFlowState.Reason -> {
|
MessageReportFlowState.Reason -> {
|
||||||
val reasons = mapOf(
|
val reasons = mapOf(
|
||||||
"Illegal" to stringResource(id = R.string.report_reason_content_illegal),
|
"Illegal" to stringResource(id = R.string.report_reason_content_illegal),
|
||||||
|
"IllegalGoods" to stringResource(id = R.string.report_reason_content_illegal_goods),
|
||||||
|
"IllegalExtortion" to stringResource(id = R.string.report_reason_content_illegal_extortion),
|
||||||
|
"IllegalPornography" to stringResource(id = R.string.report_reason_content_illegal_pornography),
|
||||||
|
"IllegalHacking" to stringResource(id = R.string.report_reason_content_illegal_hacking),
|
||||||
|
"ExtremeViolence" to stringResource(id = R.string.report_reason_content_extreme_violence),
|
||||||
"PromotesHarm" to stringResource(id = R.string.report_reason_content_promotes_harm),
|
"PromotesHarm" to stringResource(id = R.string.report_reason_content_promotes_harm),
|
||||||
|
"UnsolicitedSpam" to stringResource(id = R.string.report_reason_content_unsolicited_spam),
|
||||||
|
"Raid" to stringResource(id = R.string.report_reason_content_raid),
|
||||||
"SpamAbuse" to stringResource(id = R.string.report_reason_content_spam_abuse),
|
"SpamAbuse" to stringResource(id = R.string.report_reason_content_spam_abuse),
|
||||||
|
"ScamsFraud" to stringResource(id = R.string.report_reason_content_scams_fraud),
|
||||||
"Malware" to stringResource(id = R.string.report_reason_content_malware),
|
"Malware" to stringResource(id = R.string.report_reason_content_malware),
|
||||||
"Harassment" to stringResource(id = R.string.report_reason_content_harassment),
|
"Harassment" to stringResource(id = R.string.report_reason_content_harassment),
|
||||||
"Other" to stringResource(id = R.string.report_reason_content_other)
|
"NoneSpecified" to stringResource(id = R.string.report_reason_content_other)
|
||||||
)
|
)
|
||||||
val reasonDropdownExpanded = remember { mutableStateOf(false) }
|
val reasonDropdownExpanded = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
|
@ -91,7 +103,7 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.report),
|
text = stringResource(id = R.string.report_message_heading),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -134,38 +146,30 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Box {
|
ExposedDropdownMenuBox(
|
||||||
|
expanded = reasonDropdownExpanded.value,
|
||||||
|
onExpandedChange = {
|
||||||
|
reasonDropdownExpanded.value = it
|
||||||
|
},
|
||||||
|
) {
|
||||||
TextField(
|
TextField(
|
||||||
value = reasons[selectedReason.value]
|
value = reasons[selectedReason.value]
|
||||||
?: stringResource(id = R.string.unknown),
|
?: stringResource(id = R.string.unknown),
|
||||||
onValueChange = {
|
readOnly = true,
|
||||||
selectedReason.value = it
|
onValueChange = {},
|
||||||
},
|
|
||||||
label = {
|
label = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.report_reason)
|
text = stringResource(id = R.string.report_reason)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
readOnly = true,
|
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
IconToggleButton(
|
ExposedDropdownMenuDefaults.TrailingIcon(expanded = reasonDropdownExpanded.value)
|
||||||
checked = reasonDropdownExpanded.value,
|
|
||||||
onCheckedChange = {
|
|
||||||
reasonDropdownExpanded.value = it
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.ArrowDropDown,
|
|
||||||
contentDescription = stringResource(
|
|
||||||
id = R.string.report_reason
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
colors = ExposedDropdownMenuDefaults.textFieldColors(),
|
||||||
|
modifier = Modifier.menuAnchor()
|
||||||
)
|
)
|
||||||
|
|
||||||
DropdownMenu(
|
ExposedDropdownMenu(
|
||||||
expanded = reasonDropdownExpanded.value,
|
expanded = reasonDropdownExpanded.value,
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
reasonDropdownExpanded.value = false
|
reasonDropdownExpanded.value = false
|
||||||
|
|
@ -216,7 +220,7 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
state.value = ReportFlowState.Sending
|
state.value = MessageReportFlowState.Sending
|
||||||
},
|
},
|
||||||
modifier = Modifier.testTag("report_send")
|
modifier = Modifier.testTag("report_send")
|
||||||
) {
|
) {
|
||||||
|
|
@ -226,7 +230,7 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportFlowState.Sending -> {
|
MessageReportFlowState.Sending -> {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
title = {
|
title = {
|
||||||
|
|
@ -250,9 +254,9 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
ContentReportReason.valueOf(selectedReason.value),
|
ContentReportReason.valueOf(selectedReason.value),
|
||||||
userAddedContext.value
|
userAddedContext.value
|
||||||
)
|
)
|
||||||
state.value = ReportFlowState.Done
|
state.value = MessageReportFlowState.Done
|
||||||
} catch (e: Error) {
|
} catch (e: Error) {
|
||||||
state.value = ReportFlowState.Error
|
state.value = MessageReportFlowState.Error
|
||||||
Log.e("ReportMessageDialog", "Failed to report message", e)
|
Log.e("ReportMessageDialog", "Failed to report message", e)
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
@ -265,7 +269,7 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportFlowState.Done -> {
|
MessageReportFlowState.Done -> {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
|
|
@ -334,7 +338,7 @@ fun ReportMessageDialog(navController: NavController, messageId: String) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportFlowState.Error -> {
|
MessageReportFlowState.Error -> {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,353 @@
|
||||||
|
package chat.revolt.screens.chat.dialogs.safety
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
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.filled.ArrowDropDown
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconToggleButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import chat.revolt.R
|
||||||
|
import chat.revolt.api.RevoltAPI
|
||||||
|
import chat.revolt.api.routes.safety.putMessageReport
|
||||||
|
import chat.revolt.api.routes.safety.putUserReport
|
||||||
|
import chat.revolt.api.routes.user.blockUser
|
||||||
|
import chat.revolt.api.schemas.ContentReportReason
|
||||||
|
import chat.revolt.api.schemas.UserReportReason
|
||||||
|
import chat.revolt.components.chat.Message
|
||||||
|
import chat.revolt.components.generic.FormTextField
|
||||||
|
import chat.revolt.components.screens.settings.UserOverview
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
enum class UserReportFlowState {
|
||||||
|
Reason,
|
||||||
|
Sending,
|
||||||
|
Done,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ReportUserDialog(navController: NavController, userId: String) {
|
||||||
|
val user = RevoltAPI.userCache[userId]
|
||||||
|
if (user == null) {
|
||||||
|
navController.popBackStack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val state = remember { mutableStateOf(UserReportFlowState.Reason) }
|
||||||
|
|
||||||
|
val selectedReason = remember { mutableStateOf("UnsolicitedSpam") }
|
||||||
|
val userAddedContext = remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
when (state.value) {
|
||||||
|
UserReportFlowState.Reason -> {
|
||||||
|
val reasons = mapOf(
|
||||||
|
"UnsolicitedSpam" to stringResource(id = R.string.report_reason_user_unsolicited_spam),
|
||||||
|
"SpamAbuse" to stringResource(id = R.string.report_reason_user_spam_abuse),
|
||||||
|
"InappropriateProfile" to stringResource(id = R.string.report_reason_user_inappropriate_content),
|
||||||
|
"Impersonation" to stringResource(id = R.string.report_reason_user_impersonation),
|
||||||
|
"BanEvasion" to stringResource(id = R.string.report_reason_user_ban_evasion),
|
||||||
|
"Underage" to stringResource(id = R.string.report_reason_user_underage),
|
||||||
|
"NoneSpecified" to stringResource(id = R.string.report_reason_user_other)
|
||||||
|
)
|
||||||
|
val reasonDropdownExpanded = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
// nothing - prevent mistaps from closing the dialog
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_user_heading),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
Text(text = stringResource(id = R.string.report_user))
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_user_preview),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
|
UserOverview(user, internalPadding = false)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
ExposedDropdownMenuBox(
|
||||||
|
expanded = reasonDropdownExpanded.value,
|
||||||
|
onExpandedChange = {
|
||||||
|
reasonDropdownExpanded.value = it
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
value = reasons[selectedReason.value]
|
||||||
|
?: stringResource(id = R.string.unknown),
|
||||||
|
readOnly = true,
|
||||||
|
onValueChange = {},
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_reason)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
ExposedDropdownMenuDefaults.TrailingIcon(expanded = reasonDropdownExpanded.value)
|
||||||
|
},
|
||||||
|
colors = ExposedDropdownMenuDefaults.textFieldColors(),
|
||||||
|
modifier = Modifier.menuAnchor()
|
||||||
|
)
|
||||||
|
|
||||||
|
ExposedDropdownMenu(
|
||||||
|
expanded = reasonDropdownExpanded.value,
|
||||||
|
onDismissRequest = {
|
||||||
|
reasonDropdownExpanded.value = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
reasons.forEach { (key, value) ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Text(text = value)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
selectedReason.value = key
|
||||||
|
reasonDropdownExpanded.value = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
FormTextField(
|
||||||
|
value = userAddedContext.value,
|
||||||
|
label = stringResource(id = R.string.report_reason_additional),
|
||||||
|
onChange = {
|
||||||
|
userAddedContext.value = it
|
||||||
|
},
|
||||||
|
supportingText = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(
|
||||||
|
id = R.string.report_reason_additional_hint
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
modifier = Modifier.testTag("report_cancel")
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.report_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
state.value = UserReportFlowState.Sending
|
||||||
|
},
|
||||||
|
modifier = Modifier.testTag("report_send")
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.report_submit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
UserReportFlowState.Sending -> {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_submitting),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
LaunchedEffect(true) {
|
||||||
|
launch {
|
||||||
|
try {
|
||||||
|
Log.d("ReportMessageDialog", "Reporting user $userId")
|
||||||
|
putUserReport(
|
||||||
|
userId,
|
||||||
|
UserReportReason.valueOf(selectedReason.value),
|
||||||
|
userAddedContext.value
|
||||||
|
)
|
||||||
|
state.value = UserReportFlowState.Done
|
||||||
|
} catch (e: Error) {
|
||||||
|
state.value = UserReportFlowState.Error
|
||||||
|
Log.e("ReportMessageDialog", "Failed to report user", e)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {},
|
||||||
|
confirmButton = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
UserReportFlowState.Done -> {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Check,
|
||||||
|
contentDescription = null, // decorative
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_submit_success),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_submit_thanks),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_block_question),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
modifier = Modifier.testTag("report_block_no")
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.report_block_no))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
blockUser(userId)
|
||||||
|
}
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
modifier = Modifier.testTag("report_block_yes")
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.report_block_yes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
UserReportFlowState.Error -> {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Close,
|
||||||
|
contentDescription = null, // decorative
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_submit_error_header),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.report_submit_error),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
modifier = Modifier.testTag("report_error_ok")
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.ok))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -303,23 +303,35 @@
|
||||||
<string name="report">Report</string>
|
<string name="report">Report</string>
|
||||||
<string name="report_cancel">Cancel</string>
|
<string name="report_cancel">Cancel</string>
|
||||||
|
|
||||||
<string name="report_message">Thank you for taking the time to report this message. Please provide a reason for reporting this message.</string>
|
<string name="report_message_heading">Tell us what\'s wrong with this message</string>
|
||||||
|
<string name="report_message">Thank you for taking the time to report this message. Your report will not be shared with the user who sent the message or the server\'s moderators.</string>
|
||||||
<string name="report_message_preview">Selected message:</string>
|
<string name="report_message_preview">Selected message:</string>
|
||||||
<string name="report_message_bridge_notice">Note: This message may have been sent from another platform. It is recommended to also report the message on the platform it was sent from.</string>
|
<string name="report_message_bridge_notice">Note: This message may have been sent from another platform. It is recommended to also report the message on the platform it was sent from.</string>
|
||||||
<string name="report_server">Thank you for taking the time to report this server. Please provide a reason for reporting this server.</string>
|
<string name="report_server_heading">Tell us what\'s wrong with this server</string>
|
||||||
|
<string name="report_server">Thank you for taking the time to report this server. Your report will not be shared with the server\'s moderators.</string>
|
||||||
<string name="report_server_preview">Selected server:</string>
|
<string name="report_server_preview">Selected server:</string>
|
||||||
<string name="report_user">Thank you for taking the time to report this user. Please provide a reason for reporting this user.</string>
|
<string name="report_user_heading">Tell us what\'s wrong with this user</string>
|
||||||
|
<string name="report_user">Thank you for taking the time to report this user. Your report will not be shared with the user or the server\'s moderators.</string>
|
||||||
<string name="report_user_preview">Selected user:</string>
|
<string name="report_user_preview">Selected user:</string>
|
||||||
|
|
||||||
<string name="report_reason">Reason</string>
|
<string name="report_reason">Pick a category</string>
|
||||||
|
|
||||||
<string name="report_reason_content_illegal">Illegal content</string>
|
<string name="report_reason_content_illegal">Illegal content</string>
|
||||||
|
<string name="report_reason_content_illegal_goods">Drugs or other illegal goods</string>
|
||||||
|
<string name="report_reason_content_illegal_extortion">Extortion or blackmail</string>
|
||||||
|
<string name="report_reason_content_illegal_pornography">Revenge or underage pornography</string>
|
||||||
|
<string name="report_reason_content_illegal_hacking">Illegal hacking or cracking</string>
|
||||||
|
<string name="report_reason_content_extreme_violence">Extreme violence, gore or animal cruelty</string>
|
||||||
<string name="report_reason_content_promotes_harm">Promotes harm</string>
|
<string name="report_reason_content_promotes_harm">Promotes harm</string>
|
||||||
|
<string name="report_reason_content_unsolicited_spam">Unsolicited advertising or spam</string>
|
||||||
|
<string name="report_reason_content_raid">Raid or spam attack</string>
|
||||||
<string name="report_reason_content_spam_abuse">Spam or similar platform abuse</string>
|
<string name="report_reason_content_spam_abuse">Spam or similar platform abuse</string>
|
||||||
|
<string name="report_reason_content_scams_fraud">Scams or fraud</string>
|
||||||
<string name="report_reason_content_malware">Malware or phishing</string>
|
<string name="report_reason_content_malware">Malware or phishing</string>
|
||||||
<string name="report_reason_content_harassment">Harassment or cyberbullying</string>
|
<string name="report_reason_content_harassment">Harassment or cyberbullying</string>
|
||||||
<string name="report_reason_content_other">Other</string>
|
<string name="report_reason_content_other">Other</string>
|
||||||
|
|
||||||
|
<string name="report_reason_user_unsolicited_spam">Unsolicited advertising or spam</string>
|
||||||
<string name="report_reason_user_spam_abuse">Spam or similar platform abuse</string>
|
<string name="report_reason_user_spam_abuse">Spam or similar platform abuse</string>
|
||||||
<string name="report_reason_user_inappropriate_content">Inappropriate content (like NSFW)</string>
|
<string name="report_reason_user_inappropriate_content">Inappropriate content (like NSFW)</string>
|
||||||
<string name="report_reason_user_impersonation">Impersonation</string>
|
<string name="report_reason_user_impersonation">Impersonation</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue