add support for deletion events

This commit is contained in:
greenart7c3
2023-08-25 09:29:13 -03:00
parent e817f94045
commit b2b7799a0f
6 changed files with 78 additions and 26 deletions
@@ -432,8 +432,8 @@ class Account(
LocalCache.consume(event, null) LocalCache.consume(event, null)
} }
fun delete(note: Note) { fun delete(note: Note, signEvent: Boolean = true): DeletionEvent? {
delete(listOf(note)) return delete(listOf(note), signEvent)
} }
fun delete(notes: List<Note>, signEvent: Boolean = true): DeletionEvent? { fun delete(notes: List<Note>, signEvent: Boolean = true): DeletionEvent? {
@@ -442,11 +442,10 @@ class Account(
val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex } val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex }
if (myNotes.isNotEmpty()) { if (myNotes.isNotEmpty()) {
val event = DeletionEvent.create(myNotes, keyPair)
if (!signEvent) { if (!signEvent) {
return DeletionEvent.create(myNotes, keyPair.pubKey.toHexKey()) return event
} }
val event = DeletionEvent.create(myNotes, keyPair.privKey!!)
Client.send(event) Client.send(event)
LocalCache.consume(event) LocalCache.consume(event)
} }
@@ -334,7 +334,7 @@ private fun EditStatusBox(baseAccountUser: User, accountViewModel: AccountViewMo
UserStatusDeleteButton() { UserStatusDeleteButton() {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.updateStatus(it, "") accountViewModel.updateStatus(it, "")
accountViewModel.delete(it) accountViewModel.delete(it, true)
focusManager.clearFocus(true) focusManager.clearFocus(true)
} }
} }
@@ -37,9 +37,11 @@ import androidx.compose.material.icons.filled.Report
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
@@ -60,13 +62,17 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note 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.components.SelectTextDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog
import com.vitorpamplona.amethyst.ui.theme.WarningColor import com.vitorpamplona.amethyst.ui.theme.WarningColor
import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground
import com.vitorpamplona.quartz.events.AudioTrackEvent import com.vitorpamplona.quartz.events.AudioTrackEvent
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -200,6 +206,25 @@ private fun RenderMainPopup(
val isOwnNote = accountViewModel.isLoggedUser(note.author) val isOwnNote = accountViewModel.isLoggedUser(note.author)
val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author) val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author)
var event by remember { mutableStateOf<Event?>(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) { Popup(onDismissRequest = onDismiss, alignment = Alignment.Center) {
Card( Card(
modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape), modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape),
@@ -275,8 +300,10 @@ private fun RenderMainPopup(
) { ) {
if (accountViewModel.hideDeleteRequestDialog) { if (accountViewModel.hideDeleteRequestDialog) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.delete(note) event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber())
onDismiss() if (!accountViewModel.loggedInWithAmber()) {
onDismiss()
}
} }
} else { } else {
showDeleteAlertDialog.value = true showDeleteAlertDialog.value = true
@@ -382,6 +409,25 @@ fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) {
fun DeleteAlertDialog(note: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit) { fun DeleteAlertDialog(note: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var event by remember { mutableStateOf<Event?>(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( QuickActionAlertDialog(
title = stringResource(R.string.quick_action_request_deletion_alert_title), title = stringResource(R.string.quick_action_request_deletion_alert_title),
textContent = stringResource(R.string.quick_action_request_deletion_alert_body), 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), buttonText = stringResource(R.string.quick_action_delete_dialog_btn),
onClickDoOnce = { onClickDoOnce = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.delete(note) event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber())
}
if (!accountViewModel.loggedInWithAmber()) {
onDismiss()
} }
onDismiss()
}, },
onClickDontShowAgain = { onClickDontShowAgain = {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.delete(note) event = accountViewModel.delete(note, !accountViewModel.loggedInWithAmber())
accountViewModel.dontShowDeleteRequestDialog() accountViewModel.dontShowDeleteRequestDialog()
} }
onDismiss() if (!accountViewModel.loggedInWithAmber()) {
onDismiss()
}
}, },
onDismiss = onDismiss onDismiss = onDismiss
) )
@@ -531,7 +531,16 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
} }
Divider() Divider()
if (state.isLoggedUser) { 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)) Text(stringResource(R.string.request_deletion))
} }
} else { } else {
@@ -264,8 +264,8 @@ class AccountViewModel(val account: Account) : ViewModel() {
account.broadcast(note) account.broadcast(note)
} }
fun delete(note: Note) { fun delete(note: Note, signEvent: Boolean): DeletionEvent? {
account.delete(note) return account.delete(note, signEvent)
} }
fun decrypt(note: Note): String? { fun decrypt(note: Note): String? {
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
@Immutable @Immutable
@@ -20,20 +21,13 @@ class DeletionEvent(
companion object { companion object {
const val kind = 5 const val kind = 5
fun create(deleteEvents: List<String>, pubKey: HexKey, createdAt: Long = TimeUtils.now()): DeletionEvent { fun create(deleteEvents: List<String>, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): DeletionEvent {
val content = "" val content = ""
val pubKey = keyPair.pubKey.toHexKey()
val tags = deleteEvents.map { listOf("e", it) } val tags = deleteEvents.map { listOf("e", it) }
val id = generateId(pubKey, createdAt, kind, tags, content) val id = generateId(pubKey, createdAt, kind, tags, content)
return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
} return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
fun create(deleteEvents: List<String>, 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())
} }
} }
} }