From 6324dc64d5409426c5c054c1c43b8e701dc5f6d8 Mon Sep 17 00:00:00 2001 From: maxmoney21m Date: Wed, 8 Mar 2023 22:40:32 +0800 Subject: [PATCH] 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 +