From b2b7799a0f83b2950788e1ead46c668add60457e Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Fri, 25 Aug 2023 09:29:13 -0300 Subject: [PATCH] add support for deletion events --- .../vitorpamplona/amethyst/model/Account.kt | 9 ++- .../amethyst/ui/navigation/DrawerContent.kt | 2 +- .../amethyst/ui/note/NoteQuickActionMenu.kt | 62 +++++++++++++++++-- .../amethyst/ui/note/UserProfilePicture.kt | 11 +++- .../ui/screen/loggedIn/AccountViewModel.kt | 4 +- .../quartz/events/DeletionEvent.kt | 16 ++--- 6 files changed, 78 insertions(+), 26 deletions(-) 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 3f9307157..acef3735d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -432,8 +432,8 @@ class Account( LocalCache.consume(event, null) } - fun delete(note: Note) { - delete(listOf(note)) + fun delete(note: Note, signEvent: Boolean = true): DeletionEvent? { + return delete(listOf(note), signEvent) } fun delete(notes: List, signEvent: Boolean = true): DeletionEvent? { @@ -442,11 +442,10 @@ class Account( val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex } if (myNotes.isNotEmpty()) { + val event = DeletionEvent.create(myNotes, keyPair) if (!signEvent) { - return DeletionEvent.create(myNotes, keyPair.pubKey.toHexKey()) + return event } - - val event = DeletionEvent.create(myNotes, keyPair.privKey!!) Client.send(event) LocalCache.consume(event) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt index f180aaef9..f2f695fb1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt @@ -334,7 +334,7 @@ private fun EditStatusBox(baseAccountUser: User, accountViewModel: AccountViewMo UserStatusDeleteButton() { scope.launch(Dispatchers.IO) { accountViewModel.updateStatus(it, "") - accountViewModel.delete(it) + accountViewModel.delete(it, true) focusManager.clearFocus(true) } } 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 4a8d53283..4069b1621 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 @@ -37,9 +37,11 @@ import androidx.compose.material.icons.filled.Report import androidx.compose.material.icons.filled.Share import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +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 @@ -60,13 +62,17 @@ import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.AddressableNote +import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.service.relays.Client +import com.vitorpamplona.amethyst.ui.actions.SignerDialog import com.vitorpamplona.amethyst.ui.components.SelectTextDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog import com.vitorpamplona.amethyst.ui.theme.WarningColor import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground import com.vitorpamplona.quartz.events.AudioTrackEvent +import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.PeopleListEvent import kotlinx.coroutines.Dispatchers @@ -200,6 +206,25 @@ private fun RenderMainPopup( val isOwnNote = accountViewModel.isLoggedUser(note.author) val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author) + var event by remember { mutableStateOf(null) } + if (event != null) { + SignerDialog( + onClose = { + event = null + }, + onPost = { + scope.launch(Dispatchers.IO) { + val signedEvent = Event.fromJson(it) + Client.send(signedEvent) + LocalCache.verifyAndConsume(signedEvent, null) + event = null + onDismiss() + } + }, + data = event!!.toJson() + ) + } + Popup(onDismissRequest = onDismiss, alignment = Alignment.Center) { Card( modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape), @@ -275,8 +300,10 @@ private fun RenderMainPopup( ) { if (accountViewModel.hideDeleteRequestDialog) { scope.launch(Dispatchers.IO) { - accountViewModel.delete(note) - onDismiss() + event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) + if (!accountViewModel.loggedInWithAmber()) { + onDismiss() + } } } else { showDeleteAlertDialog.value = true @@ -382,6 +409,25 @@ fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) { fun DeleteAlertDialog(note: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit) { val scope = rememberCoroutineScope() + var event by remember { mutableStateOf(null) } + if (event != null) { + SignerDialog( + onClose = { + event = null + }, + onPost = { + scope.launch(Dispatchers.IO) { + val signedEvent = Event.fromJson(it) + Client.send(signedEvent) + LocalCache.verifyAndConsume(signedEvent, null) + event = null + onDismiss() + } + }, + data = event!!.toJson() + ) + } + QuickActionAlertDialog( title = stringResource(R.string.quick_action_request_deletion_alert_title), textContent = stringResource(R.string.quick_action_request_deletion_alert_body), @@ -389,16 +435,20 @@ fun DeleteAlertDialog(note: Note, accountViewModel: AccountViewModel, onDismiss: buttonText = stringResource(R.string.quick_action_delete_dialog_btn), onClickDoOnce = { scope.launch(Dispatchers.IO) { - accountViewModel.delete(note) + event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) + } + if (!accountViewModel.loggedInWithAmber()) { + onDismiss() } - onDismiss() }, onClickDontShowAgain = { scope.launch(Dispatchers.IO) { - accountViewModel.delete(note) + event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) accountViewModel.dontShowDeleteRequestDialog() } - onDismiss() + if (!accountViewModel.loggedInWithAmber()) { + onDismiss() + } }, onDismiss = onDismiss ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt index 5552e2013..4d65502bb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserProfilePicture.kt @@ -531,7 +531,16 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState, accountVi } Divider() if (state.isLoggedUser) { - DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.delete(note); onDismiss() } }) { + DropdownMenuItem( + onClick = { + scope.launch(Dispatchers.IO) { + event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber()) + if (!accountViewModel.loggedInWithAmber()) { + onDismiss() + } + } + } + ) { Text(stringResource(R.string.request_deletion)) } } else { 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 d565e35a6..77204af42 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 @@ -264,8 +264,8 @@ class AccountViewModel(val account: Account) : ViewModel() { account.broadcast(note) } - fun delete(note: Note) { - account.delete(note) + fun delete(note: Note, signEvent: Boolean): DeletionEvent? { + return account.delete(note, signEvent) } fun decrypt(note: Note): String? { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/DeletionEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/DeletionEvent.kt index 57ef1cf36..6d7f8a485 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/DeletionEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/DeletionEvent.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.crypto.CryptoUtils +import com.vitorpamplona.quartz.crypto.KeyPair import com.vitorpamplona.quartz.encoders.HexKey @Immutable @@ -20,20 +21,13 @@ class DeletionEvent( companion object { const val kind = 5 - fun create(deleteEvents: List, pubKey: HexKey, createdAt: Long = TimeUtils.now()): DeletionEvent { + fun create(deleteEvents: List, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): DeletionEvent { val content = "" + val pubKey = keyPair.pubKey.toHexKey() val tags = deleteEvents.map { listOf("e", it) } val id = generateId(pubKey, createdAt, kind, tags, content) - return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") - } - - fun create(deleteEvents: List, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): DeletionEvent { - val content = "" - val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() - val tags = deleteEvents.map { listOf("e", it) } - val id = generateId(pubKey, createdAt, kind, tags, content) - val sig = CryptoUtils.sign(id, privateKey) - return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) + val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey) + return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "") } } }