diff --git a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ui/note/DeferredAnimationTest.kt b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ui/note/DeferredAnimationTest.kt index 086b3e42d4..e176135a49 100644 --- a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ui/note/DeferredAnimationTest.kt +++ b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ui/note/DeferredAnimationTest.kt @@ -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), diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/GitPullRequestChanges.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/GitPullRequestChanges.kt index 04f1c257aa..280c29f59c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/GitPullRequestChanges.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/GitPullRequestChanges.kt @@ -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)) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/NewChatroomSubjectDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/NewChatroomSubjectDialog.kt index 890d697cce..dd881133d9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/NewChatroomSubjectDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/NewChatroomSubjectDialog.kt @@ -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 = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt index b63dd2940e..9fc763710b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/ephemChat/header/ShortEphemeralChatChannelHeader.kt @@ -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) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt index cb74de4208..ae56c76d54 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt @@ -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) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt index be5aa768b7..317783b3a6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt @@ -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) diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip17Dm/ui/ChatroomSubjectDialog.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip17Dm/ui/ChatroomSubjectDialog.kt index e3cb9e8c87..4273f9a3ba 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip17Dm/ui/ChatroomSubjectDialog.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip17Dm/ui/ChatroomSubjectDialog.kt @@ -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(), ) } diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip34Git/ui/GitStatusButtons.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip34Git/ui/GitStatusButtons.kt index 59e1d5c36f..73bd060df0 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip34Git/ui/GitStatusButtons.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip34Git/ui/GitStatusButtons.kt @@ -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)) diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip43RelayMembers/ui/RelayMemberCards.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip43RelayMembers/ui/RelayMemberCards.kt index 65e4e7765f..731198bcec 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip43RelayMembers/ui/RelayMemberCards.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip43RelayMembers/ui/RelayMemberCards.kt @@ -124,7 +124,7 @@ fun RelayLeaveRequestCard() { } @Composable -fun RelayMemberEventCard( +private fun RelayMemberEventCard( icon: MaterialSymbol, title: String, subtitle: String?, diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip56Reports/ui/ReportDialog.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip56Reports/ui/ReportDialog.kt index a8cd7086c1..5e738f51df 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip56Reports/ui/ReportDialog.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nip56Reports/ui/ReportDialog.kt @@ -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() }, ) diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nipsOnNostr/ui/NipCard.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nipsOnNostr/ui/NipCard.kt index 52a2f10eb4..9a3ab37730 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nipsOnNostr/ui/NipCard.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/nipsOnNostr/ui/NipCard.kt @@ -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, ) { diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/Crossfade.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/Crossfade.kt index 38c8ce3edc..0642d05d96 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/Crossfade.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/Crossfade.kt @@ -86,7 +86,7 @@ private class ChangeLatch { */ @OptIn(ExperimentalAnimationApi::class) @Composable -fun DeferredCrossfade( +internal fun DeferredCrossfade( targetState: T, modifier: Modifier, contentAlignment: Alignment, diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt index eb8b8242f6..bf08ddbbc2 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt @@ -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) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt index 8c1bec2d71..1c3fc89cf5 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt @@ -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, ) } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ReportNoteDialog.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ReportNoteDialog.kt index 673be60738..7a98ef8761 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ReportNoteDialog.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ReportNoteDialog.kt @@ -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, ) } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ShareMenu.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ShareMenu.kt index 701de331e7..fc724485cf 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ShareMenu.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/ShareMenu.kt @@ -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)