mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
Merge pull request #4189 from vitorpamplona/claude/sharp-keller-7itny0
fix: address the audit of the Render/Display split — desktop uses the shared dialogs, no blank group messages
This commit is contained in:
+4
-2
@@ -30,7 +30,7 @@ import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.v2.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.DeferredCrossfade
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -57,8 +57,10 @@ class DeferredAnimationTest {
|
||||
rule.mainClock.autoAdvance = false
|
||||
|
||||
rule.setContent {
|
||||
DeferredCrossfade(
|
||||
// enabled = true is the deferred path (DeferredCrossfade, internal to commonsUI).
|
||||
CrossfadeIfEnabled(
|
||||
targetState = state.value,
|
||||
enabled = true,
|
||||
modifier = Modifier,
|
||||
contentAlignment = Alignment.TopStart,
|
||||
animationSpec = tween(DURATION_MS),
|
||||
|
||||
+6
-6
@@ -41,8 +41,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.nip34Git.ui.CompactButtonHeight
|
||||
import com.vitorpamplona.amethyst.commons.nip34Git.ui.CompactButtonPadding
|
||||
import com.vitorpamplona.amethyst.commons.nip34Git.ui.GitCardButtonHeight
|
||||
import com.vitorpamplona.amethyst.commons.nip34Git.ui.GitCardButtonPadding
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_pr_changes_retry
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_pr_loading_changes
|
||||
@@ -108,8 +108,8 @@ fun GitPullRequestChanges(
|
||||
ChangesState.Idle ->
|
||||
FilledTonalButton(
|
||||
onClick = { load() },
|
||||
modifier = Modifier.padding(top = 8.dp).then(CompactButtonHeight),
|
||||
contentPadding = CompactButtonPadding,
|
||||
modifier = Modifier.padding(top = 8.dp).then(GitCardButtonHeight),
|
||||
contentPadding = GitCardButtonPadding,
|
||||
) {
|
||||
Icon(MaterialSymbols.Code, contentDescription = null, modifier = Modifier.size(16.dp))
|
||||
Text(stringRes(Res.string.git_pr_view_changes), style = MaterialTheme.typography.labelMedium, modifier = Modifier.padding(start = 6.dp))
|
||||
@@ -142,8 +142,8 @@ fun GitPullRequestChanges(
|
||||
ChangesState.Failed ->
|
||||
FilledTonalButton(
|
||||
onClick = { load() },
|
||||
modifier = Modifier.padding(top = 8.dp).then(CompactButtonHeight),
|
||||
contentPadding = CompactButtonPadding,
|
||||
modifier = Modifier.padding(top = 8.dp).then(GitCardButtonHeight),
|
||||
contentPadding = GitCardButtonPadding,
|
||||
) {
|
||||
Icon(MaterialSymbols.Refresh, contentDescription = null, modifier = Modifier.size(16.dp))
|
||||
Text(stringRes(Res.string.git_pr_changes_retry), style = MaterialTheme.typography.labelMedium, modifier = Modifier.padding(start = 6.dp))
|
||||
|
||||
+7
-4
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.nip17Dm.ui.ChatroomSubjectDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -36,10 +37,12 @@ fun NewChatroomSubjectDialog(
|
||||
) {
|
||||
ChatroomSubjectDialog(
|
||||
initialSubject =
|
||||
accountViewModel.account.chatroomList.rooms
|
||||
.get(room)
|
||||
?.subject
|
||||
?.value ?: "",
|
||||
remember(room) {
|
||||
accountViewModel.account.chatroomList.rooms
|
||||
.get(room)
|
||||
?.subject
|
||||
?.value ?: ""
|
||||
},
|
||||
onPost = { subject, message ->
|
||||
accountViewModel.launchSigner {
|
||||
val template =
|
||||
|
||||
+1
-2
@@ -118,14 +118,13 @@ fun ShortEphemeralChatActionOptions(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
JoinEphemeralChatButtonIfNotAlreadyJoined(channel, accountViewModel, nav)
|
||||
JoinEphemeralChatButtonIfNotAlreadyJoined(channel, accountViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun JoinEphemeralChatButtonIfNotAlreadyJoined(
|
||||
channel: EphemeralChatChannel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val isFollowing by observeUserIsFollowingChannel(accountViewModel.account, channel, accountViewModel)
|
||||
|
||||
|
||||
+1
-2
@@ -198,7 +198,7 @@ fun LongChannelActionOptions(
|
||||
|
||||
EditButtonIfIamCreator(channel, accountViewModel, nav)
|
||||
|
||||
LeaveButtonIfFollowing(channel, accountViewModel, nav)
|
||||
LeaveButtonIfFollowing(channel, accountViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -221,7 +221,6 @@ fun EditButtonIfIamCreator(
|
||||
fun LeaveButtonIfFollowing(
|
||||
channel: PublicChatChannel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val isFollowing by observeUserIsFollowingChannel(accountViewModel.account, channel, accountViewModel)
|
||||
|
||||
|
||||
+1
-2
@@ -135,14 +135,13 @@ fun ShortChannelActionOptions(
|
||||
}
|
||||
}
|
||||
|
||||
JoinChatButtonIfNotAlreadyJoined(channel, accountViewModel, nav)
|
||||
JoinChatButtonIfNotAlreadyJoined(channel, accountViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun JoinChatButtonIfNotAlreadyJoined(
|
||||
channel: PublicChatChannel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val isFollowing by observeUserIsFollowingChannel(accountViewModel.account, channel, accountViewModel)
|
||||
|
||||
|
||||
+3
-1
@@ -58,6 +58,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
/**
|
||||
* Rename a private group chat (NIP-17): edit the subject and write the message that carries the
|
||||
* change. [onPost] receives the subject (possibly blank) and the message, then the dialog closes.
|
||||
* Posting is disabled while both are blank.
|
||||
*/
|
||||
@Composable
|
||||
fun ChatroomSubjectDialog(
|
||||
@@ -94,7 +95,8 @@ fun ChatroomSubjectDialog(
|
||||
onPost(groupName.value, message.value)
|
||||
onClose()
|
||||
},
|
||||
true,
|
||||
// Nothing to send without a new subject or a message.
|
||||
isActive = groupName.value.isNotBlank() || message.value.isNotBlank(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -43,8 +43,8 @@ import com.vitorpamplona.amethyst.commons.resources.git_status_reopen
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
|
||||
/** Compact sizing shared by the small action buttons on git cards. */
|
||||
val CompactButtonHeight = Modifier.height(32.dp)
|
||||
val CompactButtonPadding = PaddingValues(horizontal = 14.dp, vertical = 4.dp)
|
||||
val GitCardButtonHeight = Modifier.height(32.dp)
|
||||
val GitCardButtonPadding = PaddingValues(horizontal = 14.dp, vertical = 4.dp)
|
||||
|
||||
/**
|
||||
* NIP-34 status buttons for an issue, patch or pull request: "Reopen" when it is
|
||||
@@ -67,8 +67,8 @@ fun GitStatusButtons(
|
||||
if (closedOrApplied) {
|
||||
FilledTonalButton(
|
||||
onClick = onReopen,
|
||||
modifier = CompactButtonHeight,
|
||||
contentPadding = CompactButtonPadding,
|
||||
modifier = GitCardButtonHeight,
|
||||
contentPadding = GitCardButtonPadding,
|
||||
) {
|
||||
Icon(MaterialSymbols.RadioButtonChecked, contentDescription = null, modifier = Modifier.size(16.dp))
|
||||
Text(stringRes(Res.string.git_status_reopen), style = MaterialTheme.typography.labelMedium, modifier = Modifier.padding(start = 6.dp))
|
||||
@@ -77,8 +77,8 @@ fun GitStatusButtons(
|
||||
if (isPatchOrPr) {
|
||||
FilledTonalButton(
|
||||
onClick = onMarkMerged,
|
||||
modifier = CompactButtonHeight,
|
||||
contentPadding = CompactButtonPadding,
|
||||
modifier = GitCardButtonHeight,
|
||||
contentPadding = GitCardButtonPadding,
|
||||
) {
|
||||
Icon(MaterialSymbols.Check, contentDescription = null, modifier = Modifier.size(16.dp))
|
||||
Text(stringRes(Res.string.git_status_mark_merged), style = MaterialTheme.typography.labelMedium, modifier = Modifier.padding(start = 6.dp))
|
||||
@@ -87,8 +87,8 @@ fun GitStatusButtons(
|
||||
OutlinedButton(
|
||||
onClick = onClose,
|
||||
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.error),
|
||||
modifier = CompactButtonHeight,
|
||||
contentPadding = CompactButtonPadding,
|
||||
modifier = GitCardButtonHeight,
|
||||
contentPadding = GitCardButtonPadding,
|
||||
) {
|
||||
Icon(MaterialSymbols.Cancel, contentDescription = null, modifier = Modifier.size(16.dp))
|
||||
Text(stringRes(Res.string.git_status_close), style = MaterialTheme.typography.labelMedium, modifier = Modifier.padding(start = 6.dp))
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ fun RelayLeaveRequestCard() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RelayMemberEventCard(
|
||||
private fun RelayMemberEventCard(
|
||||
icon: MaterialSymbol,
|
||||
title: String,
|
||||
subtitle: String?,
|
||||
|
||||
+18
-14
@@ -84,6 +84,18 @@ import com.vitorpamplona.amethyst.commons.ui.theme.LightRedColor
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/** The NIP-56 reasons the dialog offers, in display order, with their labels. */
|
||||
private val ReportReasons =
|
||||
listOf(
|
||||
ReportType.SPAM to Res.string.report_dialog_spam,
|
||||
ReportType.PROFANITY to Res.string.report_dialog_profanity,
|
||||
ReportType.IMPERSONATION to Res.string.report_dialog_impersonation,
|
||||
ReportType.NUDITY to Res.string.report_dialog_nudity,
|
||||
ReportType.ILLEGAL to Res.string.report_dialog_illegal,
|
||||
ReportType.MALWARE to Res.string.report_malware,
|
||||
ReportType.VIOLENCE to Res.string.violence,
|
||||
)
|
||||
|
||||
/**
|
||||
* Block-or-report dialog for a post. [onBlock] hides the author; [onReport] files a public
|
||||
* NIP-56 report with the chosen reason and optional text (the host also hides the author).
|
||||
@@ -96,18 +108,10 @@ fun ReportDialog(
|
||||
onReport: (reason: ReportType, additionalReason: String) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val reportTypes =
|
||||
listOf(
|
||||
Pair(ReportType.SPAM, stringRes(Res.string.report_dialog_spam)),
|
||||
Pair(ReportType.PROFANITY, stringRes(Res.string.report_dialog_profanity)),
|
||||
Pair(ReportType.IMPERSONATION, stringRes(Res.string.report_dialog_impersonation)),
|
||||
Pair(ReportType.NUDITY, stringRes(Res.string.report_dialog_nudity)),
|
||||
Pair(ReportType.ILLEGAL, stringRes(Res.string.report_dialog_illegal)),
|
||||
Pair(ReportType.MALWARE, stringRes(Res.string.report_malware)),
|
||||
Pair(ReportType.VIOLENCE, stringRes(Res.string.violence)),
|
||||
)
|
||||
|
||||
val reasonOptions = remember { reportTypes.map { TitleExplainer(it.second) }.toImmutableList() }
|
||||
val labels = ReportReasons.map { stringRes(it.second) }
|
||||
// Keyed on the labels so a locale change while the dialog is open relabels the spinner;
|
||||
// typing in the reason field recomposes with equal labels and reuses the list.
|
||||
val reasonOptions = remember(labels) { labels.map { TitleExplainer(it) }.toImmutableList() }
|
||||
var additionalReason by remember { mutableStateOf("") }
|
||||
var selectedReason by remember { mutableIntStateOf(-1) }
|
||||
|
||||
@@ -178,9 +182,9 @@ fun ReportDialog(
|
||||
ActionButton(
|
||||
text = stringRes(Res.string.report_dialog_post_report_btn),
|
||||
icon = MaterialSymbols.Report,
|
||||
enabled = selectedReason in 0..reportTypes.lastIndex,
|
||||
enabled = selectedReason in 0..ReportReasons.lastIndex,
|
||||
onClick = {
|
||||
onReport(reportTypes[selectedReason].first, additionalReason)
|
||||
onReport(ReportReasons[selectedReason].first, additionalReason)
|
||||
onDismiss()
|
||||
},
|
||||
)
|
||||
|
||||
+1
-3
@@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
@@ -142,9 +141,8 @@ fun NipCardPreview() {
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NoPaddingSuggestionChip(
|
||||
private fun NoPaddingSuggestionChip(
|
||||
label: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ private class ChangeLatch {
|
||||
*/
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun <T> DeferredCrossfade(
|
||||
internal fun <T> DeferredCrossfade(
|
||||
targetState: T,
|
||||
modifier: Modifier,
|
||||
contentAlignment: Alignment,
|
||||
|
||||
+7
-6
@@ -102,6 +102,7 @@ import com.vitorpamplona.amethyst.desktop.ui.chats.DesktopDmRoute
|
||||
import com.vitorpamplona.amethyst.desktop.ui.deck.LocalFollowPacksState
|
||||
import com.vitorpamplona.amethyst.desktop.ui.media.LightboxOverlay
|
||||
import com.vitorpamplona.amethyst.desktop.ui.note.DesktopRichText
|
||||
import com.vitorpamplona.amethyst.desktop.ui.note.ReportNoteDialog
|
||||
import com.vitorpamplona.amethyst.desktop.ui.note.RichTextCallbacks
|
||||
import com.vitorpamplona.amethyst.desktop.ui.note.WoTBadgedAvatar
|
||||
import com.vitorpamplona.amethyst.desktop.ui.profile.EditProfileDialog
|
||||
@@ -1685,19 +1686,19 @@ fun UserProfileScreen(
|
||||
}
|
||||
|
||||
if (showProfileReportDialog && iAccount != null) {
|
||||
com.vitorpamplona.amethyst.desktop.ui.note.ReportNoteDialog(
|
||||
ReportNoteDialog(
|
||||
onDismiss = { showProfileReportDialog = false },
|
||||
onReport = { type, comment ->
|
||||
onBlock = {
|
||||
scope.launch {
|
||||
try {
|
||||
iAccount.report(pubKeyHex, type, comment)
|
||||
profileSnackbar?.showSnackbar("Report sent")
|
||||
iAccount.hideUser(pubKeyHex)
|
||||
profileSnackbar?.showSnackbar("User muted")
|
||||
} catch (e: Exception) {
|
||||
profileSnackbar?.showSnackbar("Report failed: ${e.message}")
|
||||
profileSnackbar?.showSnackbar("Mute failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
},
|
||||
onBlockAndReport = { type, comment ->
|
||||
onReport = { type, comment ->
|
||||
scope.launch {
|
||||
try {
|
||||
iAccount.report(pubKeyHex, type, comment)
|
||||
|
||||
+17
-51
@@ -45,12 +45,10 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Surface
|
||||
@@ -100,6 +98,7 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState
|
||||
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.nip17Dm.ui.ChatroomSubjectDialog
|
||||
import com.vitorpamplona.amethyst.commons.richtext.CachedRichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.service.upload.CompressionQuality
|
||||
import com.vitorpamplona.amethyst.commons.service.upload.UploadOrchestrator
|
||||
@@ -1435,8 +1434,8 @@ private suspend fun sendEncryptedFiles(
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog to set or change a group's NIP-14 subject (name). Mirrors Android's
|
||||
* NewChatroomSubjectDialog: it sends a normal NIP-17 message carrying a
|
||||
* Dialog to set or change a group's NIP-14 subject (name), using the shared
|
||||
* [ChatroomSubjectDialog]: it sends a normal NIP-17 message carrying a
|
||||
* `subject` tag (plus an optional accompanying message) to every room member,
|
||||
* so all participants pick up the new name.
|
||||
*/
|
||||
@@ -1449,56 +1448,23 @@ private fun GroupSubjectDialog(
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
var groupName by remember { mutableStateOf(currentSubject) }
|
||||
var message by remember { mutableStateOf("") }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onClose,
|
||||
title = { Text("Group name") },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
OutlinedTextField(
|
||||
value = groupName,
|
||||
onValueChange = { groupName = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = { Text("Subject") },
|
||||
placeholder = { Text("A name for this group") },
|
||||
singleLine = true,
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = message,
|
||||
onValueChange = { message = it },
|
||||
modifier = Modifier.fillMaxWidth().heightIn(min = 80.dp),
|
||||
label = { Text("Message (optional)") },
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
enabled = groupName.isNotBlank(),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
try {
|
||||
val pTags = roomKey.users.mapNotNull { cacheProvider.getUserIfExists(it)?.toPTag() }
|
||||
val template =
|
||||
ChatMessageEvent.build(message, pTags) {
|
||||
groupName.ifBlank { null }?.let { changeSubject(it) }
|
||||
}
|
||||
account.sendNip17PrivateMessage(template)
|
||||
} catch (e: Exception) {
|
||||
println("Failed to set group subject: ${e.message}")
|
||||
ChatroomSubjectDialog(
|
||||
initialSubject = currentSubject,
|
||||
onPost = { subject, message ->
|
||||
scope.launch {
|
||||
try {
|
||||
val pTags = roomKey.users.mapNotNull { cacheProvider.getUserIfExists(it)?.toPTag() }
|
||||
val template =
|
||||
ChatMessageEvent.build(message, pTags) {
|
||||
subject.ifBlank { null }?.let { changeSubject(it) }
|
||||
}
|
||||
}
|
||||
onClose()
|
||||
},
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onClose) {
|
||||
Text("Cancel")
|
||||
account.sendNip17PrivateMessage(template)
|
||||
} catch (e: Exception) {
|
||||
println("Failed to set group subject: ${e.message}")
|
||||
}
|
||||
}
|
||||
},
|
||||
onClose = onClose,
|
||||
)
|
||||
}
|
||||
|
||||
+9
-75
@@ -20,90 +20,24 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.desktop.ui.note
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.RadioButton
|
||||
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.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.nip56Reports.ui.ReportDialog
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
|
||||
private val REPORT_OPTIONS =
|
||||
listOf(
|
||||
ReportType.SPAM to "Spam",
|
||||
ReportType.PROFANITY to "Profanity / Hateful speech",
|
||||
ReportType.IMPERSONATION to "Impersonation",
|
||||
ReportType.NUDITY to "Nudity / Sexual content",
|
||||
ReportType.ILLEGAL to "Illegal content",
|
||||
ReportType.MALWARE to "Malware / Phishing",
|
||||
)
|
||||
|
||||
/**
|
||||
* NIP-56 report dialog. Lets the user pick a report reason and optionally add a
|
||||
* comment, then either just report or report-and-block the author. Mirrors the
|
||||
* Android `ReportNoteDialog`, adapted to Compose Desktop.
|
||||
* NIP-56 block-or-report dialog for desktop: the shared [ReportDialog]. [onBlock] hides the
|
||||
* author; [onReport] files a public report with the chosen reason and text (callers also hide
|
||||
* the author, as Android does).
|
||||
*/
|
||||
@Composable
|
||||
fun ReportNoteDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onBlock: () -> Unit,
|
||||
onReport: (ReportType, String) -> Unit,
|
||||
onBlockAndReport: (ReportType, String) -> Unit,
|
||||
) {
|
||||
var selected by remember { mutableStateOf(REPORT_OPTIONS.first().first) }
|
||||
var comment by remember { mutableStateOf("") }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text("Report note") },
|
||||
text = {
|
||||
Column {
|
||||
REPORT_OPTIONS.forEach { (type, label) ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(selected = selected == type, onClick = { selected = type })
|
||||
.padding(vertical = 2.dp),
|
||||
) {
|
||||
RadioButton(selected = selected == type, onClick = { selected = type })
|
||||
Text(label, modifier = Modifier.padding(start = 4.dp))
|
||||
}
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = comment,
|
||||
onValueChange = { comment = it },
|
||||
label = { Text("Comment (optional)") },
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
onReport(selected, comment)
|
||||
onDismiss()
|
||||
}) { Text("Report") }
|
||||
},
|
||||
dismissButton = {
|
||||
Row {
|
||||
TextButton(onClick = {
|
||||
onBlockAndReport(selected, comment)
|
||||
onDismiss()
|
||||
}) { Text("Block & report") }
|
||||
TextButton(onClick = onDismiss) { Text("Cancel") }
|
||||
}
|
||||
},
|
||||
ReportDialog(
|
||||
onBlock = onBlock,
|
||||
onReport = onReport,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
+5
-5
@@ -159,17 +159,17 @@ fun NoteReportDialog(
|
||||
val scope = rememberCoroutineScope()
|
||||
ReportNoteDialog(
|
||||
onDismiss = onDismiss,
|
||||
onReport = { type, comment ->
|
||||
onBlock = {
|
||||
scope.launch {
|
||||
try {
|
||||
account.reportEvent(event, type, comment)
|
||||
snackbar?.showSnackbar("Report sent")
|
||||
account.hideUser(event.pubKey)
|
||||
snackbar?.showSnackbar("User muted")
|
||||
} catch (e: Exception) {
|
||||
snackbar?.showSnackbar("Report failed: ${e.message}")
|
||||
snackbar?.showSnackbar("Mute failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
},
|
||||
onBlockAndReport = { type, comment ->
|
||||
onReport = { type, comment ->
|
||||
scope.launch {
|
||||
try {
|
||||
account.reportEvent(event, type, comment)
|
||||
|
||||
Reference in New Issue
Block a user