diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/OverflowMenu.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/OverflowMenu.kt index ab54dcc70..194b8f499 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/OverflowMenu.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/controls/OverflowMenu.kt @@ -32,12 +32,9 @@ import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.PictureInPicture import androidx.compose.material.icons.filled.SaveAlt import androidx.compose.material.icons.filled.Share -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf @@ -45,9 +42,11 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.Size20Modifier @@ -134,61 +133,41 @@ fun OverflowMenuButton( modifier = Size20Modifier, ) } + } - DropdownMenu( - expanded = menuExpanded.value, - onDismissRequest = { menuExpanded.value = false }, - containerColor = Color.Black.copy(alpha = 0.85f), + if (menuExpanded.value) { + M3ActionDialog( + title = stringRes(R.string.playback_actions_dialog_title), + onDismiss = { menuExpanded.value = false }, ) { - if (showShare) { - DropdownMenuItem( - text = { Text(stringRes(R.string.share_or_save), color = Color.White) }, - onClick = { + M3ActionSection { + if (showShare) { + M3ActionRow( + icon = Icons.Default.Share, + text = stringRes(R.string.share_or_save), + ) { menuExpanded.value = false onShareClick() - }, - leadingIcon = { - Icon( - Icons.Default.Share, - contentDescription = null, - tint = Color.White, - ) - }, - ) - } - - if (showSave) { - DropdownMenuItem( - text = { Text(stringRes(R.string.download_to_phone), color = Color.White) }, - onClick = { + } + } + if (showSave) { + M3ActionRow( + icon = Icons.Default.SaveAlt, + text = stringRes(R.string.download_to_phone), + ) { menuExpanded.value = false onSaveClick() - }, - leadingIcon = { - Icon( - Icons.Default.SaveAlt, - contentDescription = null, - tint = Color.White, - ) - }, - ) - } - - if (showPip) { - DropdownMenuItem( - text = { Text(stringRes(R.string.picture_in_picture), color = Color.White) }, - onClick = { + } + } + if (showPip) { + M3ActionRow( + icon = Icons.Default.PictureInPicture, + text = stringRes(R.string.picture_in_picture), + ) { menuExpanded.value = false onPipClick() - }, - leadingIcon = { - Icon( - Icons.Default.PictureInPicture, - contentDescription = null, - tint = Color.White, - ) - }, - ) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/M3ActionDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/M3ActionDialog.kt new file mode 100644 index 000000000..38d58b2da --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/M3ActionDialog.kt @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import com.vitorpamplona.amethyst.ui.theme.Font14SP +import com.vitorpamplona.amethyst.ui.theme.Size20Modifier + +@Composable +fun M3ActionDialog( + title: String, + onDismiss: () -> Unit, + content: @Composable ColumnScope.() -> Unit, +) { + Dialog(onDismissRequest = onDismiss) { + Surface( + shape = RoundedCornerShape(28.dp), + color = MaterialTheme.colorScheme.surfaceContainerHigh, + ) { + Column( + modifier = + Modifier + .padding(vertical = 20.dp) + .verticalScroll(rememberScrollState()), + ) { + Text( + text = title, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 8.dp), + ) + content() + } + } + } +} + +@Composable +fun M3ActionSection(content: @Composable ColumnScope.() -> Unit) { + Surface( + modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp), + shape = RoundedCornerShape(16.dp), + color = MaterialTheme.colorScheme.surfaceContainerLow, + ) { + Column { + content() + } + } +} + +@Composable +fun M3ActionRow( + icon: ImageVector, + text: String, + isDestructive: Boolean = false, + enabled: Boolean = true, + onClick: () -> Unit, +) { + val tint = + if (isDestructive) { + MaterialTheme.colorScheme.error + } else { + MaterialTheme.colorScheme.onSurfaceVariant + } + val textColor = + if (isDestructive) { + MaterialTheme.colorScheme.error + } else { + MaterialTheme.colorScheme.onSurface + } + val alpha = if (enabled) 1f else 0.38f + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = + Modifier + .fillMaxWidth() + .alpha(alpha) + .clickable(enabled = enabled, role = Role.Button, onClick = onClick) + .padding(horizontal = 16.dp, vertical = 10.dp), + ) { + Icon( + imageVector = icon, + contentDescription = null, + modifier = Size20Modifier, + tint = tint, + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = text, + fontSize = Font14SP, + color = textColor, + ) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt index 79b3ce8ed..bbd56206c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/UrlPreviewCard.kt @@ -24,12 +24,11 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color @@ -47,27 +46,6 @@ import com.vitorpamplona.amethyst.ui.theme.MaxWidthWithHorzPadding import com.vitorpamplona.amethyst.ui.theme.innerPostModifier import com.vitorpamplona.amethyst.ui.theme.previewCardImageModifier -@Composable -private fun CopyToClipboard( - popupExpanded: MutableState, - content: String, - onDismiss: () -> Unit, -) { - DropdownMenu( - expanded = popupExpanded.value, - onDismissRequest = onDismiss, - ) { - val clipboardManager = LocalClipboardManager.current - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_url_to_clipboard)) }, - onClick = { - clipboardManager.setText(AnnotatedString(content)) - onDismiss() - }, - ) - } -} - @OptIn(ExperimentalFoundationApi::class) @Composable fun UrlPreviewCard( @@ -81,11 +59,20 @@ fun UrlPreviewCard( } if (popupExpanded.value) { - CopyToClipboard( - popupExpanded = popupExpanded, - content = url, + val clipboardManager = LocalClipboardManager.current + M3ActionDialog( + title = stringRes(R.string.link_actions_dialog_title), + onDismiss = { popupExpanded.value = false }, ) { - popupExpanded.value = false + M3ActionSection { + M3ActionRow( + icon = Icons.Outlined.ContentCopy, + text = stringRes(R.string.copy_url_to_clipboard), + ) { + clipboardManager.setText(AnnotatedString(url)) + popupExpanded.value = false + } + } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index e9f22e2ab..357f640ef 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -32,15 +32,16 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Report -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material.icons.outlined.Collections +import androidx.compose.material.icons.outlined.ContentCopy +import androidx.compose.material.icons.outlined.Link +import androidx.compose.material.icons.outlined.Share import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -771,119 +772,101 @@ fun ShareMediaAction( // Track if video is downloading - hoisted here to block menu dismiss during download val isDownloadingVideo = remember { mutableStateOf(false) } - DropdownMenu( - expanded = popupExpanded.value, - onDismissRequest = { if (!isDownloadingVideo.value) onDismiss() }, - ) { - val clipboardManager = LocalClipboardManager.current + if (popupExpanded.value) { + M3ActionDialog( + title = stringRes(R.string.media_actions_dialog_title), + onDismiss = { if (!isDownloadingVideo.value) onDismiss() }, + ) { + val clipboardManager = LocalClipboardManager.current - if (videoUri != null && !videoUri.startsWith("file")) { - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_url_to_clipboard)) }, - onClick = { - clipboardManager.setText(AnnotatedString(videoUri)) - onDismiss() - }, - ) - } - - postNostrUri?.let { - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_the_note_id_to_the_clipboard)) }, - onClick = { - clipboardManager.setText(AnnotatedString(it)) - onDismiss() - }, - ) - } - - postNostrUri?.let { - DropdownMenuItem( - text = { Text(stringRes(R.string.add_media_to_gallery)) }, - onClick = { - if (videoUri != null) { - val n19 = Nip19Parser.uriToRoute(postNostrUri)?.entity as? NEvent - if (n19 != null) { - accountViewModel.addMediaToGallery(n19.hex, videoUri, n19.relay.getOrNull(0), blurhash, dim, hash, mimeType) // TODO Whole list or first? - accountViewModel.toastManager.toast(R.string.media_added, R.string.media_added_to_profile_gallery) + // Copy & Gallery section + if ((videoUri != null && !videoUri.startsWith("file")) || postNostrUri != null) { + M3ActionSection { + if (videoUri != null && !videoUri.startsWith("file")) { + M3ActionRow(icon = Icons.Outlined.Link, text = stringRes(R.string.copy_url_to_clipboard)) { + clipboardManager.setText(AnnotatedString(videoUri)) + onDismiss() } } - - onDismiss() - }, - ) - } - - content?.let { - val context = LocalContext.current - - when (content) { - is MediaUrlImage -> { - videoUri?.let { - if (videoUri.isNotEmpty()) { - DropdownMenuItem( - text = { Text(stringRes(R.string.share_image)) }, - onClick = { - scope.launch { shareImageFile(context, videoUri, mimeType) } - onDismiss() - }, - ) + postNostrUri?.let { + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.copy_the_note_id_to_the_clipboard)) { + clipboardManager.setText(AnnotatedString(it)) + onDismiss() + } + M3ActionRow(icon = Icons.Outlined.Collections, text = stringRes(R.string.add_media_to_gallery)) { + if (videoUri != null) { + val n19 = Nip19Parser.uriToRoute(postNostrUri)?.entity as? NEvent + if (n19 != null) { + accountViewModel.addMediaToGallery(n19.hex, videoUri, n19.relay.getOrNull(0), blurhash, dim, hash, mimeType) + accountViewModel.toastManager.toast(R.string.media_added, R.string.media_added_to_profile_gallery) + } + } + onDismiss() } } } + } - is MediaUrlVideo -> { - videoUri?.let { - if (videoUri.isNotEmpty()) { - DropdownMenuItem( - text = { - Row(verticalAlignment = Alignment.CenterVertically) { - Text(stringRes(R.string.share_video)) - if (isDownloadingVideo.value) { - Spacer(modifier = Modifier.width(8.dp)) - LoadingAnimation(indicatorSize = 16.dp, circleWidth = 2.dp) + // Share section + content?.let { + val context = LocalContext.current + + M3ActionSection { + when (content) { + is MediaUrlImage -> { + videoUri?.let { + if (videoUri.isNotEmpty()) { + M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.share_image)) { + scope.launch { shareImageFile(context, videoUri, mimeType) } + onDismiss() + } + } + } + } + + is MediaUrlVideo -> { + videoUri?.let { + if (videoUri.isNotEmpty()) { + M3ActionRow( + icon = Icons.Outlined.Share, + text = stringRes(R.string.share_video), + enabled = !isDownloadingVideo.value, + ) { + isDownloadingVideo.value = true + scope.launch { + shareVideoFile( + context = context, + videoUrl = videoUri, + mimeType = mimeType, + okHttpClient = { url -> + accountViewModel.httpClientBuilder.okHttpClientForVideo(url) + }, + onComplete = { + isDownloadingVideo.value = false + onDismiss() + }, + onError = { + isDownloadingVideo.value = false + }, + ) } } - }, - enabled = !isDownloadingVideo.value, - onClick = { - isDownloadingVideo.value = true - scope.launch { - shareVideoFile( - context = context, - videoUrl = videoUri, - mimeType = mimeType, - okHttpClient = { url -> - accountViewModel.httpClientBuilder.okHttpClientForVideo(url) - }, - onComplete = { - isDownloadingVideo.value = false - onDismiss() - }, - onError = { - isDownloadingVideo.value = false - }, - ) - } - }, - ) + } + } } + + is MediaLocalVideo -> { + content.localFile?.let { localFile -> + M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.share_video)) { + scope.launch { shareLocalVideoFile(context, localFile, mimeType) } + onDismiss() + } + } + } + + else -> { /* No share option for other types */ } } } - - is MediaLocalVideo -> { - content.localFile?.let { localFile -> - DropdownMenuItem( - text = { Text(stringRes(R.string.share_video)) }, - onClick = { - scope.launch { shareLocalVideoFile(context, localFile, mimeType) } - onDismiss() - }, - ) - } - } - - else -> { /* No share option for other types */ } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt index aec0c4a00..242bf2f46 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt @@ -21,10 +21,22 @@ package com.vitorpamplona.amethyst.ui.note.elements import android.content.Intent -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Bookmark +import androidx.compose.material.icons.outlined.BookmarkAdd +import androidx.compose.material.icons.outlined.BookmarkRemove +import androidx.compose.material.icons.outlined.CellTower +import androidx.compose.material.icons.outlined.ContentCopy +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Edit +import androidx.compose.material.icons.outlined.Lock +import androidx.compose.material.icons.outlined.LockOpen +import androidx.compose.material.icons.outlined.PersonAdd +import androidx.compose.material.icons.outlined.PersonRemove +import androidx.compose.material.icons.outlined.PlaylistAdd +import androidx.compose.material.icons.outlined.Report +import androidx.compose.material.icons.outlined.Schedule +import androidx.compose.material.icons.outlined.Share import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.State @@ -44,6 +56,9 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.actions.EditPostView import com.vitorpamplona.amethyst.ui.components.ClickableBox import com.vitorpamplona.amethyst.ui.components.GenericLoadable +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.routeEditDraftTo @@ -53,7 +68,6 @@ import com.vitorpamplona.amethyst.ui.note.types.EditState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.report.ReportNoteDialog import com.vitorpamplona.amethyst.ui.stringRes -import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.Size24Modifier import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent @@ -78,16 +92,16 @@ fun MoreOptionsButton( onClick = { popupExpanded.value = true }, ) { VerticalDotsIcon() + } - if (popupExpanded.value) { - NoteDropDownMenu( - note = baseNote, - onDismiss = { popupExpanded.value = false }, - editState = editState, - accountViewModel = accountViewModel, - nav = nav, - ) - } + if (popupExpanded.value) { + NoteDropDownMenu( + note = baseNote, + onDismiss = { popupExpanded.value = false }, + editState = editState, + accountViewModel = accountViewModel, + nav = nav, + ) } } @@ -146,213 +160,150 @@ fun NoteDropDownMenu( ) } - DropdownMenu( - expanded = true, - onDismissRequest = onDismiss, + M3ActionDialog( + title = stringRes(R.string.note_actions_dialog_title), + onDismiss = onDismiss, ) { val clipboardManager = LocalClipboardManager.current val actContext = LocalContext.current - val scope = rememberCoroutineScope() - if (!state.isFollowingAuthor) { - DropdownMenuItem( - text = { Text(stringRes(R.string.follow)) }, - onClick = { - val author = note.author ?: return@DropdownMenuItem + // Follow section + M3ActionSection { + if (!state.isFollowingAuthor) { + M3ActionRow(icon = Icons.Outlined.PersonAdd, text = stringRes(R.string.follow)) { + val author = note.author ?: return@M3ActionRow accountViewModel.follow(author) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.unfollow)) }, - onClick = { - val author = note.author ?: return@DropdownMenuItem + } + } else { + M3ActionRow(icon = Icons.Outlined.PersonRemove, text = stringRes(R.string.unfollow)) { + val author = note.author ?: return@M3ActionRow accountViewModel.unfollow(author) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - } - DropdownMenuItem( - text = { Text(text = stringRes(R.string.follow_set_add_author_from_note_action)) }, - onClick = { - val authorHexKey = note.author?.pubkeyHex ?: return@DropdownMenuItem + } + } + M3ActionRow(icon = Icons.Outlined.PlaylistAdd, text = stringRes(R.string.follow_set_add_author_from_note_action)) { + val authorHexKey = note.author?.pubkeyHex ?: return@M3ActionRow nav.nav(Route.PeopleListManagement(authorHexKey)) onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_text)) }, - onClick = { + } + } + + // Copy & Share section + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.copy_text)) { val lastNoteVersion = (editState?.value as? GenericLoadable.Loaded)?.loaded?.modificationToShow?.value ?: note - accountViewModel.decrypt(lastNoteVersion) { - clipboardManager.setText(AnnotatedString(it)) - } + accountViewModel.decrypt(lastNoteVersion) { clipboardManager.setText(AnnotatedString(it)) } onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_user_pubkey)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.copy_user_pubkey)) { note.author?.let { scope.launch(Dispatchers.IO) { clipboardManager.setText(AnnotatedString("nostr:${it.pubkeyNpub()}")) onDismiss() } } - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_note_id)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.copy_note_id)) { scope.launch(Dispatchers.IO) { clipboardManager.setText(AnnotatedString(note.toNostrUri())) onDismiss() } - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.quick_action_share)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.quick_action_share)) { val sendIntent = Intent().apply { action = Intent.ACTION_SEND type = "text/plain" - putExtra( - Intent.EXTRA_TEXT, - externalLinkForNote(note), - ) - putExtra( - Intent.EXTRA_TITLE, - stringRes(actContext, R.string.quick_action_share_browser_link), - ) + putExtra(Intent.EXTRA_TEXT, externalLinkForNote(note)) + putExtra(Intent.EXTRA_TITLE, stringRes(actContext, R.string.quick_action_share_browser_link)) } - - val shareIntent = - Intent.createChooser(sendIntent, stringRes(actContext, R.string.quick_action_share)) + val shareIntent = Intent.createChooser(sendIntent, stringRes(actContext, R.string.quick_action_share)) ContextCompat.startActivity(actContext, shareIntent, null) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - if (state.isLoggedUser && note.isDraft()) { - DropdownMenuItem( - text = { Text(stringRes(R.string.edit_draft)) }, - onClick = { - nav.nav { - routeEditDraftTo(note, accountViewModel.account) - } - }, - ) - } - if (note.event is TextNoteEvent && !note.isDraft()) { - if (state.isLoggedUser) { - DropdownMenuItem( - text = { Text(stringRes(R.string.edit_post)) }, - onClick = { - wantsToEditPost.value = true - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.propose_an_edit)) }, - onClick = { - wantsToEditPost.value = true - }, - ) } } - DropdownMenuItem( - text = { Text(stringRes(R.string.broadcast)) }, - onClick = { + + // Edit & Broadcast section + M3ActionSection { + if (state.isLoggedUser && note.isDraft()) { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.edit_draft)) { + nav.nav { routeEditDraftTo(note, accountViewModel.account) } + } + } + if (note.event is TextNoteEvent && !note.isDraft()) { + if (state.isLoggedUser) { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.edit_post)) { + wantsToEditPost.value = true + } + } else { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.propose_an_edit)) { + wantsToEditPost.value = true + } + } + } + M3ActionRow(icon = Icons.Outlined.CellTower, text = stringRes(R.string.broadcast)) { accountViewModel.broadcast(note) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - if (accountViewModel.account.otsState.hasPendingAttestations(note)) { - DropdownMenuItem( - text = { Text(stringRes(R.string.timestamp_pending)) }, - onClick = { - onDismiss() - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.timestamp_it)) }, - onClick = { + } + } + + // Timestamp & Bookmarks section + M3ActionSection { + if (accountViewModel.account.otsState.hasPendingAttestations(note)) { + M3ActionRow(icon = Icons.Outlined.Schedule, text = stringRes(R.string.timestamp_pending)) { onDismiss() } + } else { + M3ActionRow(icon = Icons.Outlined.Schedule, text = stringRes(R.string.timestamp_it)) { accountViewModel.timestamp(note) onDismiss() - }, - ) - } - HorizontalDivider(thickness = DividerThickness) - note.let { + } + } val noteBookmarkType = if (note.event is LongTextNoteEvent) stringRes(R.string.article) else stringRes(R.string.post) - DropdownMenuItem( - text = { Text(stringRes(R.string.manage_bookmark_label, noteBookmarkType)) }, - onClick = { - if (note.event is LongTextNoteEvent) { - val noteAddress = (note as AddressableNote).address - nav.nav(Route.ArticleBookmarkManagement(noteAddress)) - } else { - nav.nav(Route.PostBookmarkManagement(note.idHex)) - } - onDismiss() - }, - ) - } - if (state.isPrivateBookmarkNote) { - DropdownMenuItem( - text = { Text(stringRes(R.string.remove_from_private_bookmarks)) }, - onClick = { + M3ActionRow(icon = Icons.Outlined.BookmarkAdd, text = stringRes(R.string.manage_bookmark_label, noteBookmarkType)) { + if (note.event is LongTextNoteEvent) { + nav.nav(Route.ArticleBookmarkManagement((note as AddressableNote).address)) + } else { + nav.nav(Route.PostBookmarkManagement(note.idHex)) + } + onDismiss() + } + if (state.isPrivateBookmarkNote) { + M3ActionRow(icon = Icons.Outlined.LockOpen, text = stringRes(R.string.remove_from_private_bookmarks)) { accountViewModel.removePrivateBookmark(note) onDismiss() - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.add_to_private_bookmarks)) }, - onClick = { + } + } else { + M3ActionRow(icon = Icons.Outlined.Lock, text = stringRes(R.string.add_to_private_bookmarks)) { accountViewModel.addPrivateBookmark(note) onDismiss() - }, - ) - } - if (state.isPublicBookmarkNote) { - DropdownMenuItem( - text = { Text(stringRes(R.string.remove_from_public_bookmarks)) }, - onClick = { + } + } + if (state.isPublicBookmarkNote) { + M3ActionRow(icon = Icons.Outlined.BookmarkRemove, text = stringRes(R.string.remove_from_public_bookmarks)) { accountViewModel.removePublicBookmark(note) onDismiss() - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.add_to_public_bookmarks)) }, - onClick = { + } + } else { + M3ActionRow(icon = Icons.Outlined.Bookmark, text = stringRes(R.string.add_to_public_bookmarks)) { accountViewModel.addPublicBookmark(note) onDismiss() - }, - ) + } + } } - HorizontalDivider(thickness = DividerThickness) - if (state.isLoggedUser) { - DropdownMenuItem( - text = { Text(stringRes(R.string.request_deletion)) }, - onClick = { + + // Moderation section + M3ActionSection { + if (state.isLoggedUser) { + M3ActionRow(icon = Icons.Outlined.Delete, text = stringRes(R.string.request_deletion), isDestructive = true) { accountViewModel.delete(note) onDismiss() - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.block_report)) }, - onClick = { reportDialogShowing = true }, - ) + } + } else { + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.block_report), isDestructive = true) { + reportDialogShowing = true + } + } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupItemOptions.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupItemOptions.kt index c2411a8c9..717499410 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupItemOptions.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupItemOptions.kt @@ -21,10 +21,19 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarkgroups.display import android.content.Intent -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.BookmarkRemove +import androidx.compose.material.icons.outlined.CellTower +import androidx.compose.material.icons.outlined.ContentCopy +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Edit +import androidx.compose.material.icons.outlined.Lock +import androidx.compose.material.icons.outlined.LockOpen +import androidx.compose.material.icons.outlined.PersonAdd +import androidx.compose.material.icons.outlined.PersonRemove +import androidx.compose.material.icons.outlined.Report +import androidx.compose.material.icons.outlined.Schedule +import androidx.compose.material.icons.outlined.Share import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.getValue @@ -42,6 +51,9 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.actions.EditPostView import com.vitorpamplona.amethyst.ui.components.ClickableBox import com.vitorpamplona.amethyst.ui.components.GenericLoadable +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.routeEditDraftTo import com.vitorpamplona.amethyst.ui.note.VerticalDotsIcon @@ -52,7 +64,6 @@ import com.vitorpamplona.amethyst.ui.note.types.EditState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.report.ReportNoteDialog import com.vitorpamplona.amethyst.ui.stringRes -import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.Size24Modifier import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import kotlinx.coroutines.Dispatchers @@ -76,20 +87,20 @@ fun BookmarkGroupItemOptions( onClick = { popupExpanded.value = true }, ) { VerticalDotsIcon() + } - if (popupExpanded.value) { - BookmarkGroupItemOptionsMenu( - note = baseNote, - isBookmarkItemPrivate = isBookmarkItemPrivate, - onDismiss = { popupExpanded.value = false }, - onMoveBookmarkToPublic = onMoveBookmarkToPublic, - onMoveBookmarkToPrivate = onMoveBookmarkToPrivate, - onDeleteBookmarkItem = onDeleteBookmarkItem, - editState = editState, - accountViewModel = accountViewModel, - nav = nav, - ) - } + if (popupExpanded.value) { + BookmarkGroupItemOptionsMenu( + note = baseNote, + isBookmarkItemPrivate = isBookmarkItemPrivate, + onDismiss = { popupExpanded.value = false }, + onMoveBookmarkToPublic = onMoveBookmarkToPublic, + onMoveBookmarkToPrivate = onMoveBookmarkToPrivate, + onDeleteBookmarkItem = onDeleteBookmarkItem, + editState = editState, + accountViewModel = accountViewModel, + nav = nav, + ) } } @@ -142,89 +153,71 @@ fun BookmarkGroupItemOptionsMenu( ) } - DropdownMenu( - expanded = true, - onDismissRequest = onDismiss, + M3ActionDialog( + title = stringRes(R.string.bookmark_item_actions_dialog_title), + onDismiss = onDismiss, ) { val clipboardManager = LocalClipboardManager.current val actContext = LocalContext.current - val scope = rememberCoroutineScope() - DropdownMenuItem( - text = { Text(stringRes(if (isBookmarkItemPrivate) R.string.move_bookmark_to_public_label else R.string.move_bookmark_to_private_label)) }, - onClick = if (isBookmarkItemPrivate) onMoveBookmarkToPublic else onMoveBookmarkToPrivate, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.bookmark_remove_action_label)) }, - onClick = { + + // Bookmark Management section + M3ActionSection { + M3ActionRow( + icon = if (isBookmarkItemPrivate) Icons.Outlined.LockOpen else Icons.Outlined.Lock, + text = stringRes(if (isBookmarkItemPrivate) R.string.move_bookmark_to_public_label else R.string.move_bookmark_to_private_label), + ) { + if (isBookmarkItemPrivate) onMoveBookmarkToPublic() else onMoveBookmarkToPrivate() + } + M3ActionRow( + icon = Icons.Outlined.BookmarkRemove, + text = stringRes(R.string.bookmark_remove_action_label), + isDestructive = true, + ) { onDeleteBookmarkItem() onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) + } + } - if (!state.isFollowingAuthor) { - DropdownMenuItem( - text = { Text(stringRes(R.string.follow)) }, - onClick = { - val author = note.author ?: return@DropdownMenuItem + // Follow section + M3ActionSection { + if (!state.isFollowingAuthor) { + M3ActionRow(icon = Icons.Outlined.PersonAdd, text = stringRes(R.string.follow)) { + val author = note.author ?: return@M3ActionRow accountViewModel.follow(author) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.unfollow)) }, - onClick = { - val author = note.author ?: return@DropdownMenuItem + } + } else { + M3ActionRow(icon = Icons.Outlined.PersonRemove, text = stringRes(R.string.unfollow)) { + val author = note.author ?: return@M3ActionRow accountViewModel.unfollow(author) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - } -// DropdownMenuItem( -// text = { Text(text = stringRes(R.string.follow_set_add_author_from_note_action)) }, -// onClick = { -// val authorHexKey = note.author?.pubkeyHex ?: return@DropdownMenuItem -// nav.nav(Route.PeopleListManagement(authorHexKey)) -// onDismiss() -// }, -// ) - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_text)) }, - onClick = { - val lastNoteVersion = (editState?.value as? GenericLoadable.Loaded)?.loaded?.modificationToShow?.value ?: note - accountViewModel.decrypt(lastNoteVersion) { - clipboardManager.setText(AnnotatedString(it)) } + } + } + + // Copy & Share section + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.copy_text)) { + val lastNoteVersion = (editState?.value as? GenericLoadable.Loaded)?.loaded?.modificationToShow?.value ?: note + accountViewModel.decrypt(lastNoteVersion) { clipboardManager.setText(AnnotatedString(it)) } onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_user_pubkey)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.copy_user_pubkey)) { note.author?.let { scope.launch(Dispatchers.IO) { clipboardManager.setText(AnnotatedString("nostr:${it.pubkeyNpub()}")) onDismiss() } } - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_note_id)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.copy_note_id)) { scope.launch(Dispatchers.IO) { clipboardManager.setText(AnnotatedString(note.toNostrUri())) onDismiss() } - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.quick_action_share)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.quick_action_share)) { val sendIntent = Intent().apply { action = Intent.ACTION_SEND @@ -243,74 +236,57 @@ fun BookmarkGroupItemOptionsMenu( Intent.createChooser(sendIntent, stringRes(actContext, R.string.quick_action_share)) ContextCompat.startActivity(actContext, shareIntent, null) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - if (state.isLoggedUser && note.isDraft()) { - DropdownMenuItem( - text = { Text(stringRes(R.string.edit_draft)) }, - onClick = { + } + } + + // Edit & Broadcast section + M3ActionSection { + if (state.isLoggedUser && note.isDraft()) { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.edit_draft)) { nav.nav { routeEditDraftTo(note, accountViewModel.account) } - }, - ) - } - if (note.event is TextNoteEvent && !note.isDraft()) { - if (state.isLoggedUser) { - DropdownMenuItem( - text = { Text(stringRes(R.string.edit_post)) }, - onClick = { - wantsToEditPost.value = true - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.propose_an_edit)) }, - onClick = { - wantsToEditPost.value = true - }, - ) + } } - } - DropdownMenuItem( - text = { Text(stringRes(R.string.broadcast)) }, - onClick = { + if (note.event is TextNoteEvent && !note.isDraft()) { + if (state.isLoggedUser) { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.edit_post)) { + wantsToEditPost.value = true + } + } else { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.propose_an_edit)) { + wantsToEditPost.value = true + } + } + } + M3ActionRow(icon = Icons.Outlined.CellTower, text = stringRes(R.string.broadcast)) { accountViewModel.broadcast(note) onDismiss() - }, - ) - HorizontalDivider(thickness = DividerThickness) - if (accountViewModel.account.otsState.hasPendingAttestations(note)) { - DropdownMenuItem( - text = { Text(stringRes(R.string.timestamp_pending)) }, - onClick = { + } + } + + // Timestamp & Moderation section + M3ActionSection { + if (accountViewModel.account.otsState.hasPendingAttestations(note)) { + M3ActionRow(icon = Icons.Outlined.Schedule, text = stringRes(R.string.timestamp_pending)) { onDismiss() - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.timestamp_it)) }, - onClick = { + } + } else { + M3ActionRow(icon = Icons.Outlined.Schedule, text = stringRes(R.string.timestamp_it)) { accountViewModel.timestamp(note) onDismiss() - }, - ) - } - HorizontalDivider(thickness = DividerThickness) - if (state.isLoggedUser) { - DropdownMenuItem( - text = { Text(stringRes(R.string.request_deletion)) }, - onClick = { + } + } + if (state.isLoggedUser) { + M3ActionRow(icon = Icons.Outlined.Delete, text = stringRes(R.string.request_deletion), isDestructive = true) { accountViewModel.delete(note) onDismiss() - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(R.string.block_report)) }, - onClick = { reportDialogShowing = true }, - ) + } + } else { + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.block_report), isDestructive = true) { + reportDialogShowing = true + } + } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupScreen.kt index 1a7605f9b..943a262ab 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/display/BookmarkGroupScreen.kt @@ -30,11 +30,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.CellTower +import androidx.compose.material.icons.outlined.Delete import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme @@ -57,6 +57,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.components.ClickableBox +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon @@ -65,7 +68,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarkgroups.BookmarkType import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.ButtonBorder -import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.TabRowHeight import kotlinx.coroutines.launch @@ -304,6 +306,31 @@ fun BookmarkGroupActionsMenuButton( ) { val isActionListOpen = remember { mutableStateOf(false) } + if (isActionListOpen.value) { + M3ActionDialog( + title = stringRes(R.string.list_actions_dialog_title), + onDismiss = { isActionListOpen.value = false }, + ) { + M3ActionSection { + M3ActionRow( + icon = Icons.Outlined.CellTower, + text = stringRes(R.string.bookmark_list_broadcast_btn_label), + ) { + onBroadcastList() + isActionListOpen.value = false + } + M3ActionRow( + icon = Icons.Outlined.Delete, + text = stringRes(R.string.bookmark_list_delete_btn_label), + isDestructive = true, + ) { + onDeleteList() + isActionListOpen.value = false + } + } + } + } + ClickableBox( modifier = StdPadding @@ -319,44 +346,5 @@ fun BookmarkGroupActionsMenuButton( onClick = { isActionListOpen.value = true }, ) { VerticalDotsIcon() - BookmarkGroupActionsMenu( - onCloseMenu = { isActionListOpen.value = false }, - isOpen = isActionListOpen.value, - onBroadcastList = onBroadcastList, - onDeleteList = onDeleteList, - ) - } -} - -@Composable -fun BookmarkGroupActionsMenu( - onCloseMenu: () -> Unit, - isOpen: Boolean, - onBroadcastList: () -> Unit, - onDeleteList: () -> Unit, -) { - DropdownMenu( - expanded = isOpen, - onDismissRequest = onCloseMenu, - ) { - DropdownMenuItem( - text = { - Text(stringRes(R.string.bookmark_list_broadcast_btn_label)) - }, - onClick = { - onBroadcastList() - onCloseMenu() - }, - ) - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { - Text(stringRes(R.string.bookmark_list_delete_btn_label)) - }, - onClick = { - onDeleteList() - onCloseMenu() - }, - ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/list/BookmarkGroupItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/list/BookmarkGroupItem.kt index 2393d385b..eeb80529f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/list/BookmarkGroupItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/list/BookmarkGroupItem.kt @@ -32,10 +32,12 @@ import androidx.compose.foundation.layout.offset import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.Article import androidx.compose.material.icons.outlined.CollectionsBookmark +import androidx.compose.material.icons.outlined.ContentCopy +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Description +import androidx.compose.material.icons.outlined.Edit import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.ListItem @@ -57,6 +59,9 @@ import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkList import com.vitorpamplona.amethyst.ui.components.ClickableBox +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.note.VerticalDotsIcon import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarkgroups.BookmarkType import com.vitorpamplona.amethyst.ui.stringRes @@ -258,18 +263,18 @@ private fun BookmarkGroupOptionsButton( onClick = { isMenuOpen.value = true }, ) { VerticalDotsIcon() - - GroupOptionsMenu( - groupName = bookmarkGroupName, - groupDescription = bookmarkGroupDescription, - isExpanded = isMenuOpen.value, - onDismiss = { isMenuOpen.value = false }, - onGroupRename = onGroupRename, - onGroupDescriptionChange = onGroupDescriptionChange, - onGroupClone = onGroupCloneCreate, - onDelete = onGroupDelete, - ) } + + GroupOptionsMenu( + groupName = bookmarkGroupName, + groupDescription = bookmarkGroupDescription, + isExpanded = isMenuOpen.value, + onDismiss = { isMenuOpen.value = false }, + onGroupRename = onGroupRename, + onGroupDescriptionChange = onGroupDescriptionChange, + onGroupClone = onGroupCloneCreate, + onDelete = onGroupDelete, + ) } @Composable @@ -288,45 +293,32 @@ private fun GroupOptionsMenu( val optionalCloneName = remember { mutableStateOf(null) } val optionalCloneDescription = remember { mutableStateOf(null) } - DropdownMenu( - expanded = isExpanded, - onDismissRequest = onDismiss, - ) { - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.follow_set_rename_btn_label)) - }, - onClick = { - onGroupRename() - onDismiss() - }, - ) - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.follow_set_desc_modify_label)) - }, - onClick = { - onGroupDescriptionChange() - onDismiss() - }, - ) - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.follow_set_copy_action_btn_label)) - }, - onClick = { - isCopyDialogOpen.value = true - onDismiss() - }, - ) - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.quick_action_delete)) - }, - onClick = { - onDelete() - }, - ) + if (isExpanded) { + M3ActionDialog( + title = stringRes(R.string.group_actions_dialog_title), + onDismiss = onDismiss, + ) { + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.follow_set_rename_btn_label)) { + onGroupRename() + onDismiss() + } + M3ActionRow(icon = Icons.Outlined.Description, text = stringRes(R.string.follow_set_desc_modify_label)) { + onGroupDescriptionChange() + onDismiss() + } + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.follow_set_copy_action_btn_label)) { + isCopyDialogOpen.value = true + onDismiss() + } + } + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Delete, text = stringRes(R.string.quick_action_delete), isDestructive = true) { + onDelete() + onDismiss() + } + } + } } if (isCopyDialogOpen.value) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/BookmarkGroupManagementItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/BookmarkGroupManagementItem.kt index 756b8bc8e..a66a30be7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/BookmarkGroupManagementItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/BookmarkGroupManagementItem.kt @@ -30,12 +30,11 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BookmarkAdd import androidx.compose.material.icons.filled.BookmarkRemove +import androidx.compose.material.icons.outlined.BookmarkAdd import androidx.compose.material.icons.outlined.CollectionsBookmark import androidx.compose.material.icons.outlined.Lock import androidx.compose.material.icons.outlined.Public import androidx.compose.material.icons.outlined.RemoveCircleOutline -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem @@ -48,6 +47,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarkgroups.list.BookmarkMembershipStatusAndNumberDisplay import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.HalfHalfVertPadding @@ -158,6 +160,30 @@ fun BookmarkManagementOptions( ) { val isBookmarkAddTapped = remember { mutableStateOf(false) } + if (isBookmarkAddTapped.value) { + M3ActionDialog( + title = stringRes(R.string.add_bookmark_dialog_title), + onDismiss = { isBookmarkAddTapped.value = false }, + ) { + M3ActionSection { + M3ActionRow( + icon = Icons.Outlined.BookmarkAdd, + text = stringRes(R.string.public_bookmark_add_action_label), + ) { + onAddBookmark(false) + isBookmarkAddTapped.value = false + } + M3ActionRow( + icon = Icons.Outlined.Lock, + text = stringRes(R.string.private_bookmark_add_action_label), + ) { + onAddBookmark(true) + isBookmarkAddTapped.value = false + } + } + } + } + Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, @@ -196,29 +222,5 @@ fun BookmarkManagementOptions( ) } } - - DropdownMenu( - expanded = isBookmarkAddTapped.value, - onDismissRequest = { isBookmarkAddTapped.value = false }, - ) { - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.public_bookmark_add_action_label)) - }, - onClick = { - onAddBookmark(false) - isBookmarkAddTapped.value = false - }, - ) - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.private_bookmark_add_action_label)) - }, - onClick = { - onAddBookmark(true) - isBookmarkAddTapped.value = false - }, - ) - } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt index 5bf51f508..8b5961be5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListTabs.kt @@ -28,8 +28,9 @@ import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material.icons.outlined.DoneAll +import androidx.compose.material.icons.outlined.Groups +import androidx.compose.material.icons.outlined.MoveToInbox import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -48,6 +49,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.components.zonedDrawerSwipe import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -103,12 +107,13 @@ fun MessagesTabHeader( contentDescription = stringRes(id = R.string.more_options), tint = MaterialTheme.colorScheme.placeholderText, ) + } - MessagesTabMenu( - moreActionsExpanded, - { moreActionsExpanded = false }, - onMarkKnownAsRead, - onMarkNewAsRead, + if (moreActionsExpanded) { + MessagesMarkAsReadDialog( + onDismiss = { moreActionsExpanded = false }, + onMarkKnownAsRead = onMarkKnownAsRead, + onMarkNewAsRead = onMarkNewAsRead, ) } } @@ -142,34 +147,38 @@ fun MessagesPager( } @Composable -fun MessagesTabMenu( - expanded: Boolean, +fun MessagesMarkAsReadDialog( onDismiss: () -> Unit, onMarkKnownAsRead: () -> Unit, onMarkNewAsRead: () -> Unit, ) { - DropdownMenu(expanded = expanded, onDismissRequest = onDismiss) { - DropdownMenuItem( - text = { Text(stringRes(R.string.mark_all_known_as_read)) }, - onClick = { + M3ActionDialog( + title = stringRes(R.string.mark_as_read_dialog_title), + onDismiss = onDismiss, + ) { + M3ActionSection { + M3ActionRow( + icon = Icons.Outlined.Groups, + text = stringRes(R.string.mark_all_known_as_read), + ) { onMarkKnownAsRead() onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.mark_all_new_as_read)) }, - onClick = { + } + M3ActionRow( + icon = Icons.Outlined.MoveToInbox, + text = stringRes(R.string.mark_all_new_as_read), + ) { onMarkNewAsRead() onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.mark_all_as_read)) }, - onClick = { + } + M3ActionRow( + icon = Icons.Outlined.DoneAll, + text = stringRes(R.string.mark_all_as_read), + ) { onMarkKnownAsRead() onMarkNewAsRead() onDismiss() - }, - ) + } + } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/lists/PeopleListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/lists/PeopleListScreen.kt index 5c0c0602a..95b23a2ae 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/lists/PeopleListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/lists/PeopleListScreen.kt @@ -38,11 +38,14 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.CellTower +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Edit +import androidx.compose.material.icons.outlined.Share import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults.cardElevation -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.IconButton @@ -75,6 +78,9 @@ import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.components.ClickableBox +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route @@ -416,15 +422,16 @@ private fun ListActionsMenuButton( onClick = { isActionListOpen.value = true }, ) { VerticalDotsIcon() + } - DropdownMenu( - expanded = isActionListOpen.value, - onDismissRequest = { isActionListOpen.value = false }, + if (isActionListOpen.value) { + val context = LocalContext.current + M3ActionDialog( + title = stringRes(R.string.list_actions_dialog_title), + onDismiss = { isActionListOpen.value = false }, ) { - val context = LocalContext.current - DropdownMenuItem( - text = { Text(stringRes(R.string.quick_action_share)) }, - onClick = { + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.quick_action_share)) { val sendIntent = Intent().apply { action = Intent.ACTION_SEND @@ -443,38 +450,22 @@ private fun ListActionsMenuButton( Intent.createChooser(sendIntent, stringRes(context, R.string.quick_action_share)) ContextCompat.startActivity(context, shareIntent, null) isActionListOpen.value = false - }, - ) - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { - Text(stringRes(R.string.follow_set_edit_list_metadata)) - }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.follow_set_edit_list_metadata)) { onEditList() isActionListOpen.value = false - }, - ) - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { - Text(stringRes(R.string.follow_set_broadcast)) - }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.CellTower, text = stringRes(R.string.follow_set_broadcast)) { onBroadcastList() isActionListOpen.value = false - }, - ) - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { - Text(stringRes(R.string.follow_set_delete)) - }, - onClick = { + } + } + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Delete, text = stringRes(R.string.follow_set_delete), isDestructive = true) { onDeleteList() isActionListOpen.value = false - }, - ) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/packs/FollowPackScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/packs/FollowPackScreen.kt index eefa908ad..c10fbc237 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/packs/FollowPackScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/packs/FollowPackScreen.kt @@ -35,11 +35,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.CellTower +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Edit +import androidx.compose.material.icons.outlined.Share import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults.cardElevation -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.IconButton @@ -68,6 +71,9 @@ import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.components.ClickableBox +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route @@ -271,15 +277,16 @@ private fun ListActionsMenuButton( onClick = { isActionListOpen.value = true }, ) { VerticalDotsIcon() + } - DropdownMenu( - expanded = isActionListOpen.value, - onDismissRequest = { isActionListOpen.value = false }, + if (isActionListOpen.value) { + val context = LocalContext.current + M3ActionDialog( + title = stringRes(R.string.pack_actions_dialog_title), + onDismiss = { isActionListOpen.value = false }, ) { - val context = LocalContext.current - DropdownMenuItem( - text = { Text(stringRes(R.string.quick_action_share)) }, - onClick = { + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.quick_action_share)) { val sendIntent = Intent().apply { action = Intent.ACTION_SEND @@ -298,38 +305,22 @@ private fun ListActionsMenuButton( Intent.createChooser(sendIntent, stringRes(context, R.string.quick_action_share)) ContextCompat.startActivity(context, shareIntent, null) isActionListOpen.value = false - }, - ) - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { - Text(stringRes(R.string.follow_pack_edit_list_metadata)) - }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.follow_pack_edit_list_metadata)) { onEditList() isActionListOpen.value = false - }, - ) - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { - Text(stringRes(R.string.follow_pack_broadcast)) - }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.CellTower, text = stringRes(R.string.follow_pack_broadcast)) { onBroadcastList() isActionListOpen.value = false - }, - ) - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { - Text(stringRes(R.string.follow_pack_delete)) - }, - onClick = { + } + } + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Delete, text = stringRes(R.string.follow_pack_delete), isDestructive = true) { onDeleteList() isActionListOpen.value = false - }, - ) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt index 369852526..2268ff2e0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt @@ -29,13 +29,14 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.offset import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ContentCopy +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Groups import androidx.compose.material.icons.outlined.Lock import androidx.compose.material.icons.outlined.Public import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.Text @@ -59,6 +60,9 @@ import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.PeopleList import com.vitorpamplona.amethyst.ui.components.ClickableBox +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.note.VerticalDotsIcon import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer @@ -286,15 +290,15 @@ private fun PeopleListOptionsButton( onClick = { isMenuOpen.value = true }, ) { VerticalDotsIcon() - - ListOptionsMenu( - isExpanded = isMenuOpen.value, - onListEditMetadata = onListEditMetadata, - onListClone = onListCloneCreate, - onDelete = onListDelete, - onDismiss = { isMenuOpen.value = false }, - ) } + + ListOptionsMenu( + isExpanded = isMenuOpen.value, + onListEditMetadata = onListEditMetadata, + onListClone = onListCloneCreate, + onDelete = onListDelete, + onDismiss = { isMenuOpen.value = false }, + ) } @Composable @@ -309,36 +313,28 @@ private fun ListOptionsMenu( val optionalCloneName = remember { mutableStateOf(null) } val optionalCloneDescription = remember { mutableStateOf(null) } - DropdownMenu( - expanded = isExpanded, - onDismissRequest = onDismiss, - ) { - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.follow_set_edit_list_metadata)) - }, - onClick = { - onListEditMetadata() - onDismiss() - }, - ) - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.follow_set_copy_action_btn_label)) - }, - onClick = { - isCopyDialogOpen.value = true - onDismiss() - }, - ) - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.quick_action_delete)) - }, - onClick = { - onDelete() - }, - ) + if (isExpanded) { + M3ActionDialog( + title = stringRes(R.string.list_management_dialog_title), + onDismiss = onDismiss, + ) { + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Edit, text = stringRes(R.string.follow_set_edit_list_metadata)) { + onListEditMetadata() + onDismiss() + } + M3ActionRow(icon = Icons.Outlined.ContentCopy, text = stringRes(R.string.follow_set_copy_action_btn_label)) { + isCopyDialogOpen.value = true + onDismiss() + } + } + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Delete, text = stringRes(R.string.quick_action_delete), isDestructive = true) { + onDelete() + onDismiss() + } + } + } } if (isCopyDialogOpen.value) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/PeopleListAndUserItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/PeopleListAndUserItem.kt index f7e4db06b..7f37e0dad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/PeopleListAndUserItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/PeopleListAndUserItem.kt @@ -33,10 +33,9 @@ import androidx.compose.material.icons.filled.PersonAdd import androidx.compose.material.icons.filled.PersonRemove import androidx.compose.material.icons.outlined.Groups import androidx.compose.material.icons.outlined.Lock +import androidx.compose.material.icons.outlined.PersonAdd import androidx.compose.material.icons.outlined.Public import androidx.compose.material.icons.outlined.RemoveCircleOutline -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem @@ -50,6 +49,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.list.DisplayParticipantNumberAndStatus import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.HalfHalfVertPadding @@ -193,6 +195,30 @@ private fun UserAdditionOptions( ) { val isUserAddTapped = remember { mutableStateOf(false) } + if (isUserAddTapped.value) { + M3ActionDialog( + title = stringRes(R.string.add_member_dialog_title), + onDismiss = { isUserAddTapped.value = false }, + ) { + M3ActionSection { + M3ActionRow( + icon = Icons.Outlined.PersonAdd, + text = stringRes(R.string.follow_set_public_member_add_label), + ) { + onAddUserToList(false) + isUserAddTapped.value = false + } + M3ActionRow( + icon = Icons.Outlined.Lock, + text = stringRes(R.string.follow_set_private_member_add_label), + ) { + onAddUserToList(true) + isUserAddTapped.value = false + } + } + } + } + Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, @@ -231,29 +257,5 @@ private fun UserAdditionOptions( ) } } - - DropdownMenu( - expanded = isUserAddTapped.value, - onDismissRequest = { isUserAddTapped.value = false }, - ) { - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.follow_set_public_member_add_label)) - }, - onClick = { - onAddUserToList(false) - isUserAddTapped.value = false - }, - ) - DropdownMenuItem( - text = { - Text(text = stringRes(R.string.follow_set_private_member_add_label)) - }, - onClick = { - onAddUserToList(true) - isUserAddTapped.value = false - }, - ) - } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/UserProfileDropDownMenu.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/UserProfileDropDownMenu.kt index d299d5aef..0dd30e5ee 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/UserProfileDropDownMenu.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/UserProfileDropDownMenu.kt @@ -21,20 +21,24 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header import android.content.Intent -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Block +import androidx.compose.material.icons.outlined.CheckCircle +import androidx.compose.material.icons.outlined.ContentCopy +import androidx.compose.material.icons.outlined.Report +import androidx.compose.material.icons.outlined.Share import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.AnnotatedString import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.User +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.note.externalLinkForUser import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.stringRes -import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.quartz.nip56Reports.ReportType @Composable @@ -44,108 +48,94 @@ fun UserProfileDropDownMenu( onDismiss: () -> Unit, accountViewModel: AccountViewModel, ) { - DropdownMenu( - expanded = popupExpanded, - onDismissRequest = onDismiss, + if (!popupExpanded) return + + M3ActionDialog( + title = stringRes(R.string.profile_actions_dialog_title), + onDismiss = onDismiss, ) { val clipboardManager = LocalClipboardManager.current - - DropdownMenuItem( - text = { Text(stringRes(R.string.copy_user_id)) }, - onClick = { - clipboardManager.setText(AnnotatedString(user.pubkeyNpub())) - onDismiss() - }, - ) - val context = LocalContext.current - DropdownMenuItem( - text = { Text(stringRes(R.string.quick_action_share)) }, - onClick = { + // Share section + M3ActionSection { + M3ActionRow( + icon = Icons.Outlined.ContentCopy, + text = stringRes(R.string.copy_user_id), + ) { + clipboardManager.setText(AnnotatedString(user.pubkeyNpub())) + onDismiss() + } + M3ActionRow( + icon = Icons.Outlined.Share, + text = stringRes(R.string.quick_action_share), + ) { val sendIntent = Intent().apply { action = Intent.ACTION_SEND type = "text/plain" - putExtra( - Intent.EXTRA_TEXT, - externalLinkForUser(user), - ) + putExtra(Intent.EXTRA_TEXT, externalLinkForUser(user)) putExtra( Intent.EXTRA_TITLE, stringRes(context, R.string.quick_action_share_browser_link), ) } - - val shareIntent = - Intent.createChooser(sendIntent, stringRes(context, R.string.quick_action_share)) + val shareIntent = Intent.createChooser(sendIntent, stringRes(context, R.string.quick_action_share)) context.startActivity(shareIntent) onDismiss() - }, - ) + } + } + // Moderation section (if not self) if (accountViewModel.userProfile() != user) { - HorizontalDivider(thickness = DividerThickness) - if (accountViewModel.account.isHidden(user)) { - DropdownMenuItem( - text = { Text(stringRes(R.string.unblock_user)) }, - onClick = { + M3ActionSection { + if (accountViewModel.account.isHidden(user)) { + M3ActionRow( + icon = Icons.Outlined.CheckCircle, + text = stringRes(R.string.unblock_user), + ) { accountViewModel.show(user) onDismiss() - }, - ) - } else { - DropdownMenuItem( - text = { Text(stringRes(id = R.string.block_hide_user)) }, - onClick = { + } + } else { + M3ActionRow( + icon = Icons.Outlined.Block, + text = stringRes(R.string.block_hide_user), + isDestructive = true, + ) { accountViewModel.hide(user) onDismiss() - }, - ) + } + } } - HorizontalDivider(thickness = DividerThickness) - DropdownMenuItem( - text = { Text(stringRes(id = R.string.report_spam_scam)) }, - onClick = { + + // Report section + M3ActionSection { + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.report_spam_scam), isDestructive = true) { accountViewModel.report(user, ReportType.SPAM) onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.report_hateful_speech)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.report_hateful_speech), isDestructive = true) { accountViewModel.report(user, ReportType.PROFANITY) onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(id = R.string.report_impersonation)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.report_impersonation), isDestructive = true) { accountViewModel.report(user, ReportType.IMPERSONATION) onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.report_nudity_porn)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.report_nudity_porn), isDestructive = true) { accountViewModel.report(user, ReportType.NUDITY) onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(id = R.string.report_illegal_behaviour)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.report_illegal_behaviour), isDestructive = true) { accountViewModel.report(user, ReportType.ILLEGAL) onDismiss() - }, - ) - DropdownMenuItem( - text = { Text(stringRes(id = R.string.report_malware)) }, - onClick = { + } + M3ActionRow(icon = Icons.Outlined.Report, text = stringRes(R.string.report_malware), isDestructive = true) { accountViewModel.report(user, ReportType.MALWARE) onDismiss() - }, - ) + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt index e45562fe9..c4d65da7b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt @@ -29,8 +29,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Share -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material.icons.outlined.Description +import androidx.compose.material.icons.outlined.FolderZip import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -53,6 +53,9 @@ import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.DefaultDMRelayList import com.vitorpamplona.amethyst.model.DefaultIndexerRelayList import com.vitorpamplona.amethyst.model.DefaultSearchRelayList +import com.vitorpamplona.amethyst.ui.components.M3ActionDialog +import com.vitorpamplona.amethyst.ui.components.M3ActionRow +import com.vitorpamplona.amethyst.ui.components.M3ActionSection import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -490,6 +493,7 @@ fun SettingsCategoryWithButton( @Composable fun ExportDropdownMenu(collection: () -> RelayListCollection) { var expanded by remember { mutableStateOf(false) } + val context = LocalContext.current IconButton(onClick = { expanded = true }) { Icon( @@ -498,24 +502,27 @@ fun ExportDropdownMenu(collection: () -> RelayListCollection) { ) } - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - val context = LocalContext.current - DropdownMenuItem( - text = { Text(stringRes(R.string.export_as_text)) }, - onClick = { - expanded = false - RelayExporter(context).export(collection()) - }, - ) - DropdownMenuItem( - text = { Text(stringRes(R.string.export_as_zip)) }, - onClick = { - expanded = false - RelayZipExporter(context).export(collection()) - }, - ) + if (expanded) { + M3ActionDialog( + title = stringRes(R.string.export_actions_dialog_title), + onDismiss = { expanded = false }, + ) { + M3ActionSection { + M3ActionRow( + icon = Icons.Outlined.Description, + text = stringRes(R.string.export_as_text), + ) { + expanded = false + RelayExporter(context).export(collection()) + } + M3ActionRow( + icon = Icons.Outlined.FolderZip, + text = stringRes(R.string.export_as_zip), + ) { + expanded = false + RelayZipExporter(context).export(collection()) + } + } + } } } diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index c3787c3c0..27d5402e2 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1878,6 +1878,21 @@ Error Completed + + Mark as Read + Note Actions + Profile Actions + Media Actions + Playback + Pack Actions + List Actions + Bookmark Actions + Group Actions + Add Bookmark + Add Member + List Management + Link Actions + Export Attestation Valid Invalid