From 6324dc64d5409426c5c054c1c43b8e701dc5f6d8 Mon Sep 17 00:00:00 2001 From: maxmoney21m Date: Wed, 8 Mar 2023 22:40:32 +0800 Subject: [PATCH 1/6] Add NoteQuickActionMenu composable cf #71 --- .../ui/components/SelectTextDialog.kt | 57 ++++++ .../amethyst/ui/note/NoteQuickActionMenu.kt | 171 ++++++++++++++++++ .../text_select_move_forward_character.xml | 5 + app/src/main/res/values/strings.xml | 16 +- 4 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt create mode 100644 app/src/main/res/drawable/text_select_move_forward_character.xml diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt new file mode 100644 index 000000000..69bc90918 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt @@ -0,0 +1,57 @@ +package com.vitorpamplona.amethyst.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.Card +import androidx.compose.material.Divider +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import com.vitorpamplona.amethyst.R + +@Composable +fun SelectTextDialog(text: String, onDismiss: () -> Unit) { + Dialog( + onDismissRequest = onDismiss, + ) { + Card { + Column { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.End + ) { + IconButton( + onClick = onDismiss, + modifier = Modifier.background(MaterialTheme.colors.background) + ) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = null, + tint = MaterialTheme.colors.primary + ) + } + Text(text = stringResource(R.string.select_text)) + } + Divider() + Row(modifier = Modifier.padding(16.dp)) { + SelectionContainer { + Text(text) + } + } + } + } + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt new file mode 100644 index 000000000..2973c45e8 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt @@ -0,0 +1,171 @@ +package com.vitorpamplona.amethyst.ui.note + +import android.content.Intent +import android.widget.Toast +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Divider +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AlternateEmail +import androidx.compose.material.icons.filled.ContentCopy +import androidx.compose.material.icons.filled.Share +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Popup +import androidx.core.content.ContextCompat +import androidx.core.graphics.ColorUtils +import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.components.SelectTextDialog +import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import kotlinx.coroutines.launch + +fun lightenColor(color: Color, amount: Float): Color { + var argb = color.toArgb() + val hslOut = floatArrayOf(0f, 0f, 0f) + ColorUtils.colorToHSL(argb, hslOut) + hslOut[2] += amount + argb = ColorUtils.HSLToColor(hslOut) + return Color(argb) +} + +val externalLinkForNote = { note: Note -> "https://snort.social/e/${note.idNote()}" } + +@Composable +fun VerticalDivider(color: Color) = + Divider( + color = color, modifier = Modifier + .fillMaxHeight() + .width(1.dp) + ) + +@Composable +fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit, accountViewModel: AccountViewModel) { + val context = LocalContext.current + val primaryLight = lightenColor(MaterialTheme.colors.primary, 0.2f) + val cardShape = RoundedCornerShape(5.dp) + val clipboardManager = LocalClipboardManager.current + val scope = rememberCoroutineScope() + var showSelectTextDialog by remember { mutableStateOf(false) } + + val showToast = { stringResource: Int -> + scope.launch { + Toast.makeText( + context, + context.getString(stringResource), + Toast.LENGTH_SHORT + ).show() + } + } + + if (popupExpanded) { + Popup(onDismissRequest = onDismiss) { + Card( + modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape), + shape = cardShape, + backgroundColor = MaterialTheme.colors.primary, + ) { + Column(modifier = Modifier.width(IntrinsicSize.Min)) { + Row(modifier = Modifier.height(IntrinsicSize.Min)) { + NoteQuickActionItem( + icon = Icons.Default.ContentCopy, + label = stringResource(R.string.quick_action_copy_text) + ) { + clipboardManager.setText( + AnnotatedString( + accountViewModel.decrypt(note) ?: "" + ) + ) + showToast(R.string.copied_note_text_to_clipboard) + onDismiss() + } + VerticalDivider(primaryLight) + NoteQuickActionItem(Icons.Default.AlternateEmail, stringResource(R.string.quick_action_copy_note_id)) { + clipboardManager.setText(AnnotatedString("@${note.idNote()}")) + showToast(R.string.copied_note_id_to_clipboard) + onDismiss() + } + VerticalDivider(primaryLight) + NoteQuickActionItem( + icon = ImageVector.vectorResource(id = R.drawable.text_select_move_forward_character), + label = stringResource(R.string.quick_action_select) + ) { + showSelectTextDialog = true + onDismiss() + } + VerticalDivider(primaryLight) + NoteQuickActionItem(icon = Icons.Default.Share, label = stringResource(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, context.getString(R.string.quick_action_share_browser_link)) + } + + val shareIntent = Intent.createChooser(sendIntent, context.getString(R.string.quick_action_share)) + ContextCompat.startActivity(context, shareIntent, null) + onDismiss() + } + } + } + } + } + } + + if (showSelectTextDialog) { + accountViewModel.decrypt(note)?.let { + SelectTextDialog(it) { showSelectTextDialog = false } + } + } +} + +@Composable +fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) { + Column( + modifier = Modifier + .size(64.dp) + .clickable { onClick() }, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + imageVector = icon, + contentDescription = null, + modifier = Modifier.size(20.dp), + tint = MaterialTheme.colors.onPrimary, + ) + Text(text = label, fontSize = 12.sp) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/text_select_move_forward_character.xml b/app/src/main/res/drawable/text_select_move_forward_character.xml new file mode 100644 index 000000000..312d40dbc --- /dev/null +++ b/app/src/main/res/drawable/text_select_move_forward_character.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eb90867a7..aa56b00a7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - + Amethyst Amethyst Debug Point to the QR Code @@ -20,7 +20,7 @@ Relay Icon Unknown Author Copy Text - Copy User PubKey + Copy Author @npub Copy Note ID Broadcast @@ -177,7 +177,7 @@ Mark all Known as read Mark all New as read Mark all as read - + ## Key Backup and Safety Tips \n\nYour account is secured by a secret key. The key is long random string starting with **nsec1**. Anyone who has access to your secret key can publish content using your identity. \n\n- Do **not** put your secret key in any website or software you do not trust. @@ -192,4 +192,12 @@ "Badge award image for %1$s" You Received a new Badge Award Badge award granted to - \ No newline at end of file + Copied note text to clipboard + Copied note ID (@note1) to clipboard + Select Text + Select + Share Browser Link + Share + Copy ID + Copy Text + From 496736125762bbfd4878e0c8979ecf3f7d8f2eb8 Mon Sep 17 00:00:00 2001 From: maxmoney21m Date: Wed, 8 Mar 2023 22:41:30 +0800 Subject: [PATCH 2/6] Note long click action opens NoteQuickActionMenu --- .../vitorpamplona/amethyst/ui/note/NoteCompose.kt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 4da4d92db..df29b16bd 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -54,8 +54,8 @@ import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.TranslateableRichTextViewer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader import com.vitorpamplona.amethyst.ui.theme.Following +import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader @OptIn(ExperimentalFoundationApi::class) @Composable @@ -431,7 +431,7 @@ fun NoteCompose( ) } - NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel) + NoteQuickActionMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel) } } } @@ -756,15 +756,9 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit, } Divider() } - DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(accountViewModel.decrypt(note) ?: "")); onDismiss() }) { - Text(stringResource(R.string.copy_text)) - } - DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(note.author?.pubkeyNpub() ?: "")); onDismiss() }) { + DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}" ?: "")); onDismiss() }) { Text(stringResource(R.string.copy_user_pubkey)) } - DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(note.idNote())); onDismiss() }) { - Text(stringResource(R.string.copy_note_id)) - } Divider() DropdownMenuItem(onClick = { accountViewModel.broadcast(note); onDismiss() }) { Text(stringResource(R.string.broadcast)) From 15771289e253d30ec34b4912a4639d2dc632426e Mon Sep 17 00:00:00 2001 From: maxmoney21m Date: Wed, 8 Mar 2023 23:30:41 +0800 Subject: [PATCH 3/6] Revise note quick action button layout --- .../amethyst/ui/components/SelectTextDialog.kt | 2 +- .../amethyst/ui/note/NoteCompose.kt | 7 +------ .../amethyst/ui/note/NoteQuickActionMenu.kt | 17 ++++++++++++++--- app/src/main/res/values/strings.xml | 8 +++++--- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt index 69bc90918..15061da9e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt @@ -43,7 +43,7 @@ fun SelectTextDialog(text: String, onDismiss: () -> Unit) { tint = MaterialTheme.colors.primary ) } - Text(text = stringResource(R.string.select_text)) + Text(text = stringResource(R.string.select_text_dialog_top)) } Divider() Row(modifier = Modifier.padding(16.dp)) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index df29b16bd..045887019 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -54,8 +53,8 @@ import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status import com.vitorpamplona.amethyst.ui.components.ResizeImage import com.vitorpamplona.amethyst.ui.components.TranslateableRichTextViewer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.theme.Following import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader +import com.vitorpamplona.amethyst.ui.theme.Following @OptIn(ExperimentalFoundationApi::class) @Composable @@ -756,10 +755,6 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit, } Divider() } - DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}" ?: "")); onDismiss() }) { - Text(stringResource(R.string.copy_user_pubkey)) - } - Divider() DropdownMenuItem(onClick = { accountViewModel.broadcast(note); onDismiss() }) { Text(stringResource(R.string.broadcast)) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt index 2973c45e8..61e07a774 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -20,6 +21,7 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AlternateEmail import androidx.compose.material.icons.filled.ContentCopy +import androidx.compose.material.icons.filled.FormatQuote import androidx.compose.material.icons.filled.Share import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -109,12 +111,20 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni onDismiss() } VerticalDivider(primaryLight) - NoteQuickActionItem(Icons.Default.AlternateEmail, stringResource(R.string.quick_action_copy_note_id)) { + NoteQuickActionItem(Icons.Default.AlternateEmail, stringResource(R.string.quick_action_copy_user_id)) { + clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}" ?: "")) + showToast(R.string.copied_user_id_to_clipboard) + onDismiss() + } + VerticalDivider(primaryLight) + NoteQuickActionItem(Icons.Default.FormatQuote, stringResource(R.string.quick_action_copy_note_id)) { clipboardManager.setText(AnnotatedString("@${note.idNote()}")) showToast(R.string.copied_note_id_to_clipboard) onDismiss() } - VerticalDivider(primaryLight) + } + Divider(color = primaryLight, modifier = Modifier.fillMaxWidth().width(1.dp)) + Row(modifier = Modifier.height(IntrinsicSize.Min)) { NoteQuickActionItem( icon = ImageVector.vectorResource(id = R.drawable.text_select_move_forward_character), label = stringResource(R.string.quick_action_select) @@ -138,6 +148,7 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni ContextCompat.startActivity(context, shareIntent, null) onDismiss() } + VerticalDivider(primaryLight) } } } @@ -163,7 +174,7 @@ fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) { Icon( imageVector = icon, contentDescription = null, - modifier = Modifier.size(20.dp), + modifier = Modifier.size(24.dp), tint = MaterialTheme.colors.onPrimary, ) Text(text = label, fontSize = 12.sp) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aa56b00a7..dae1ded26 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -193,11 +193,13 @@ You Received a new Badge Award Badge award granted to Copied note text to clipboard + Copied author’s @npub to clipboard Copied note ID (@note1) to clipboard - Select Text + Select Text Select Share Browser Link Share - Copy ID - Copy Text + Mention + Quote + Copy From 32cd5f7eefc6d2aeb3d0d23c6bbea72bec6db6f7 Mon Sep 17 00:00:00 2001 From: maxmoney21m Date: Wed, 8 Mar 2023 23:39:37 +0800 Subject: [PATCH 4/6] Fix linting errors --- .../amethyst/ui/components/SelectTextDialog.kt | 2 +- .../amethyst/ui/note/NoteQuickActionMenu.kt | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt index 15061da9e..bd9d488b9 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SelectTextDialog.kt @@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.R @Composable fun SelectTextDialog(text: String, onDismiss: () -> Unit) { Dialog( - onDismissRequest = onDismiss, + onDismissRequest = onDismiss ) { Card { Column { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt index 61e07a774..c3d449aa0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt @@ -65,7 +65,8 @@ val externalLinkForNote = { note: Note -> "https://snort.social/e/${note.idNote( @Composable fun VerticalDivider(color: Color) = Divider( - color = color, modifier = Modifier + color = color, + modifier = Modifier .fillMaxHeight() .width(1.dp) ) @@ -94,7 +95,7 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni Card( modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape), shape = cardShape, - backgroundColor = MaterialTheme.colors.primary, + backgroundColor = MaterialTheme.colors.primary ) { Column(modifier = Modifier.width(IntrinsicSize.Min)) { Row(modifier = Modifier.height(IntrinsicSize.Min)) { @@ -169,14 +170,14 @@ fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) { .size(64.dp) .clickable { onClick() }, verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, + horizontalAlignment = Alignment.CenterHorizontally ) { Icon( imageVector = icon, contentDescription = null, modifier = Modifier.size(24.dp), - tint = MaterialTheme.colors.onPrimary, + tint = MaterialTheme.colors.onPrimary ) Text(text = label, fontSize = 12.sp) } -} \ No newline at end of file +} From 07e4c82c1b5eaba0c08d99a15bf6d11478ca595c Mon Sep 17 00:00:00 2001 From: maxmoney21m Date: Thu, 9 Mar 2023 01:24:43 +0800 Subject: [PATCH 5/6] Add (un)follow/delete to note quick actions --- .../amethyst/ui/note/NoteQuickActionMenu.kt | 30 ++++++++++++++++++- .../ui/screen/loggedIn/AccountViewModel.kt | 8 +++++ app/src/main/res/values/strings.xml | 3 ++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt index c3d449aa0..d570e1d76 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt @@ -21,7 +21,10 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AlternateEmail import androidx.compose.material.icons.filled.ContentCopy +import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.FormatQuote +import androidx.compose.material.icons.filled.PersonAdd +import androidx.compose.material.icons.filled.PersonRemove import androidx.compose.material.icons.filled.Share import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -79,6 +82,8 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni val clipboardManager = LocalClipboardManager.current val scope = rememberCoroutineScope() var showSelectTextDialog by remember { mutableStateOf(false) } + val isOwnNote = note.author == accountViewModel.userProfile() + val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author!!) val showToast = { stringResource: Int -> scope.launch { @@ -124,8 +129,31 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni onDismiss() } } - Divider(color = primaryLight, modifier = Modifier.fillMaxWidth().width(1.dp)) + Divider( + color = primaryLight, + modifier = Modifier + .fillMaxWidth() + .width(1.dp) + ) Row(modifier = Modifier.height(IntrinsicSize.Min)) { + if (isOwnNote) { + NoteQuickActionItem(Icons.Default.Delete, stringResource(R.string.quick_action_delete)) { + accountViewModel.delete(note) + onDismiss() + } + } else if (isFollowingUser) { + NoteQuickActionItem(Icons.Default.PersonRemove, stringResource(R.string.quick_action_unfollow)) { + accountViewModel.unfollow(note.author!!) + onDismiss() + } + } else { + NoteQuickActionItem(Icons.Default.PersonAdd, stringResource(R.string.quick_action_follow)) { + accountViewModel.follow(note.author!!) + onDismiss() + } + } + + VerticalDivider(primaryLight) NoteQuickActionItem( icon = ImageVector.vectorResource(id = R.drawable.text_select_move_forward_character), label = stringResource(R.string.quick_action_select) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 2b36d2fbb..9e9e5e78e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -120,4 +120,12 @@ class AccountViewModel(private val account: Account) : ViewModel() { fun follow(user: User) { account.follow(user) } + + fun unfollow(user: User) { + account.unfollow(user) + } + + fun isFollowing(user: User): Boolean { + return account.userProfile().isFollowing(user) + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dae1ded26..5335ee5f5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -202,4 +202,7 @@ Mention Quote Copy + Delete + Unfollow + Follow From 9e58ef111061dad6c2699ab682c5c6b9c34b7dd3 Mon Sep 17 00:00:00 2001 From: maxmoney21m Date: Thu, 9 Mar 2023 01:56:54 +0800 Subject: [PATCH 6/6] Show alert first time deleting a note --- .../amethyst/LocalPreferences.kt | 5 ++ .../vitorpamplona/amethyst/model/Account.kt | 6 +++ .../amethyst/ui/note/NoteQuickActionMenu.kt | 46 ++++++++++++++++++- .../ui/screen/loggedIn/AccountViewModel.kt | 8 ++++ app/src/main/res/values/strings.xml | 2 + 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt index f0dfe6331..abec38a63 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt @@ -25,6 +25,7 @@ class LocalPreferences(context: Context) { const val TRANSLATE_TO = "translateTo" const val ZAP_AMOUNTS = "zapAmounts" const val LATEST_CONTACT_LIST = "latestContactList" + const val HIDE_DELETE_REQUEST_INFO = "hideDeleteRequestInfo" val LAST_READ: (String) -> String = { route -> "last_read_route_$route" } } @@ -49,6 +50,7 @@ class LocalPreferences(context: Context) { account.translateTo.let { putString(PrefKeys.TRANSLATE_TO, it) } account.zapAmountChoices.let { putString(PrefKeys.ZAP_AMOUNTS, gson.toJson(it)) } account.backupContactList.let { putString(PrefKeys.LATEST_CONTACT_LIST, Event.gson.toJson(it)) } + putBoolean(PrefKeys.HIDE_DELETE_REQUEST_INFO, account.hideDeleteRequestInfo) }.apply() } @@ -89,6 +91,8 @@ class LocalPreferences(context: Context) { mapOf() } + val hideDeleteRequestInfo = getBoolean(PrefKeys.HIDE_DELETE_REQUEST_INFO, false) + if (pubKey != null) { return Account( Persona(privKey = privKey?.toByteArray(), pubKey = pubKey.toByteArray()), @@ -99,6 +103,7 @@ class LocalPreferences(context: Context) { languagePreferences, translateTo, zapAmountChoices, + hideDeleteRequestInfo, latestContactList ) } else { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index ebafe6b5c..92a9a7f1c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -56,6 +56,7 @@ class Account( var languagePreferences: Map = mapOf(), var translateTo: String = Locale.getDefault().language, var zapAmountChoices: List = listOf(500L, 1000L, 5000L), + var hideDeleteRequestInfo: Boolean = false, var backupContactList: ContactListEvent? = null ) { var transientHiddenUsers: Set = setOf() @@ -540,6 +541,11 @@ class Account( saveable.invalidateData() } + fun setHideDeleteRequestInfo() { + hideDeleteRequestInfo = true + saveable.invalidateData() + } + init { backupContactList?.let { println("Loading saved contacts ${it.toJson()}") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt index d570e1d76..bf2d910d2 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt @@ -10,14 +10,18 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.AlertDialog +import androidx.compose.material.Button import androidx.compose.material.Card import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Text +import androidx.compose.material.TextButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AlternateEmail import androidx.compose.material.icons.filled.ContentCopy @@ -82,6 +86,7 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni val clipboardManager = LocalClipboardManager.current val scope = rememberCoroutineScope() var showSelectTextDialog by remember { mutableStateOf(false) } + var showDeleteAlertDialog by remember { mutableStateOf(false) } val isOwnNote = note.author == accountViewModel.userProfile() val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author!!) @@ -138,8 +143,12 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni Row(modifier = Modifier.height(IntrinsicSize.Min)) { if (isOwnNote) { NoteQuickActionItem(Icons.Default.Delete, stringResource(R.string.quick_action_delete)) { - accountViewModel.delete(note) - onDismiss() + if (accountViewModel.hideDeleteRequestInfo()) { + accountViewModel.delete(note) + onDismiss() + } else { + showDeleteAlertDialog = true + } } } else if (isFollowingUser) { NoteQuickActionItem(Icons.Default.PersonRemove, stringResource(R.string.quick_action_unfollow)) { @@ -189,6 +198,39 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni SelectTextDialog(it) { showSelectTextDialog = false } } } + + if (showDeleteAlertDialog) { + AlertDialog( + onDismissRequest = { onDismiss() }, + title = { + Text(text = stringResource(R.string.quick_action_request_deletion_alert_title)) + }, + text = { + Text(text = stringResource(R.string.quick_action_request_deletion_alert_body)) + }, + buttons = { + Row( + modifier = Modifier.padding(all = 8.dp).fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + TextButton( + onClick = { + accountViewModel.setHideDeleteRequestInfo() + accountViewModel.delete(note) + onDismiss() + } + ) { + Text("Don't show again") + } + Button( + onClick = { accountViewModel.delete(note); onDismiss() } + ) { + Text("Delete") + } + } + } + ) + } } @Composable diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 9e9e5e78e..2734c6ee1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -128,4 +128,12 @@ class AccountViewModel(private val account: Account) : ViewModel() { fun isFollowing(user: User): Boolean { return account.userProfile().isFollowing(user) } + + fun hideDeleteRequestInfo(): Boolean { + return account.hideDeleteRequestInfo + } + + fun setHideDeleteRequestInfo() { + account.setHideDeleteRequestInfo() + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5335ee5f5..bf1bebabe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -205,4 +205,6 @@ Delete Unfollow Follow + Request Deletion + Amethyst will request that your note be deleted from the relays you are currently connected to. There is no guarantee that your note will be permanently deleted from those relays, or from other relays where it may be stored.