From 9ee1250f3ab8bcaaf2729a647e80024db7008911 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Wed, 30 Aug 2023 14:15:20 -0300 Subject: [PATCH] unblock users --- .../vitorpamplona/amethyst/model/Account.kt | 17 +- .../amethyst/ui/note/ZapNoteCompose.kt | 113 ++++++++++++- .../ui/screen/loggedIn/AccountViewModel.kt | 4 + .../ui/screen/loggedIn/ProfileScreen.kt | 155 +++++++++++++++++- .../quartz/events/PeopleListEvent.kt | 18 ++ 5 files changed, 297 insertions(+), 10 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 1bfac4864..4d755123e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -117,6 +117,7 @@ class Account( keyPair.pubKey.toHexKey() ) blockList?.decryptedContent = AmberUtils.content + live.invalidateData() AmberUtils.content = "" } @@ -1684,7 +1685,7 @@ class Account( return returningList } - fun hideUser(pubkeyHex: String, encryptedContent: String): PeopleListEvent? { + fun hideUser(pubkeyHex: String, encryptedContent: String): PeopleListEvent { val blockList = migrateHiddenUsersIfNeeded(getBlockList()) return if (blockList != null) { @@ -1732,6 +1733,20 @@ class Account( saveable.invalidateData() } + fun showUser(pubkeyHex: String, encryptedContent: String): PeopleListEvent? { + val blockList = migrateHiddenUsersIfNeeded(getBlockList()) + if (blockList != null) { + return PeopleListEvent.removeUser( + earlierVersion = blockList, + pubKeyHex = pubkeyHex, + isPrivate = true, + pubKey = keyPair.pubKey.toHexKey(), + encryptedContent = encryptedContent + ) + } + return null + } + fun showUser(pubkeyHex: String) { val blockList = migrateHiddenUsersIfNeeded(getBlockList()) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt index e5443107b..4ddde43c0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt @@ -1,6 +1,9 @@ package com.vitorpamplona.amethyst.ui.note +import android.app.Activity import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -26,11 +29,14 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ServiceManager import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User +import com.vitorpamplona.amethyst.service.AmberUtils import com.vitorpamplona.amethyst.service.relays.Client import com.vitorpamplona.amethyst.ui.actions.SignerDialog +import com.vitorpamplona.amethyst.ui.actions.SignerType import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.FollowButton @@ -41,6 +47,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.showAmountAxis import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.Size55dp import com.vitorpamplona.amethyst.ui.theme.placeholderText +import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.events.ContactListEvent import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.LnZapEvent @@ -177,10 +184,114 @@ fun UserActionOptions( baseAuthor: User, accountViewModel: AccountViewModel ) { + val scope = rememberCoroutineScope() + val context = LocalContext.current + + 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) + accountViewModel.account.live.invalidateData() + accountViewModel.account.saveable.invalidateData() + event = null + } + }, + data = event!!.toJson() + ) + } + + val encryptResult = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult(), + onResult = { + if (it.resultCode != Activity.RESULT_OK) { + scope.launch { + Toast.makeText( + context, + "Sign request rejected", + Toast.LENGTH_SHORT + ).show() + } + return@rememberLauncherForActivityResult + } + + val encryptedContent = it.data?.getStringExtra("signature") ?: "" + event = accountViewModel.show(baseAuthor, encryptedContent) + } + ) + + val decryptResult = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult(), + onResult = { + if (it.resultCode != Activity.RESULT_OK) { + scope.launch { + Toast.makeText( + context, + "Sign request rejected", + Toast.LENGTH_SHORT + ).show() + } + return@rememberLauncherForActivityResult + } + + val decryptedContent = it.data?.getStringExtra("signature") ?: "" + val blockList = accountViewModel.account.getBlockList() + val privateTags = if (blockList == null) { + listOf(listOf("p", baseAuthor.pubkeyHex)) + } else { + if (accountViewModel.account.isHidden(baseAuthor)) { + blockList.privateTagsOrEmpty(decryptedContent).filter { element -> !element.contains(baseAuthor.pubkeyHex) } + } else { + blockList.privateTagsOrEmpty(decryptedContent).plus(element = listOf("p", baseAuthor.pubkeyHex)) + } + } + val msg = Event.mapper.writeValueAsString(privateTags) + + ServiceManager.shouldPauseService = true + AmberUtils.openAmber( + msg, + SignerType.NIP04_ENCRYPT, + encryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } + ) + WatchIsHiddenUser(baseAuthor, accountViewModel) { isHidden -> if (isHidden) { ShowUserButton { - accountViewModel.show(baseAuthor) + if (accountViewModel.loggedInWithAmber()) { + scope.launch(Dispatchers.IO) { + val blockList = accountViewModel.account.getBlockList() + val content = blockList?.content ?: "" + if (content.isBlank()) { + val privateTags = listOf(listOf("p", baseAuthor.pubkeyHex)) + val msg = Event.mapper.writeValueAsString(privateTags) + + AmberUtils.openAmber( + msg, + SignerType.NIP04_ENCRYPT, + encryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } else { + AmberUtils.openAmber( + content, + SignerType.NIP04_DECRYPT, + decryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } + } + } else { + accountViewModel.show(baseAuthor) + } } } else { ShowFollowingOrUnfollowingButton(baseAuthor, accountViewModel) 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 44f902538..0efb3be88 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 @@ -467,6 +467,10 @@ class AccountViewModel(val account: Account) : ViewModel() { return account.unseal(event) } + fun show(user: User, encryptedContent: String): PeopleListEvent? { + return account.showUser(user.pubkeyHex, encryptedContent) + } + fun show(user: User) { viewModelScope.launch(Dispatchers.IO) { account.showUser(user.pubkeyHex) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt index 4dc0280e9..6d7b7c512 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt @@ -727,10 +727,114 @@ private fun ProfileActions( EditButton(accountViewModel.account) } + val scope = rememberCoroutineScope() + val context = LocalContext.current + + 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) + accountViewModel.account.live.invalidateData() + accountViewModel.account.saveable.invalidateData() + event = null + } + }, + data = event!!.toJson() + ) + } + + val encryptResult = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult(), + onResult = { + if (it.resultCode != Activity.RESULT_OK) { + scope.launch { + Toast.makeText( + context, + "Sign request rejected", + Toast.LENGTH_SHORT + ).show() + } + return@rememberLauncherForActivityResult + } + + val encryptedContent = it.data?.getStringExtra("signature") ?: "" + event = accountViewModel.show(baseUser, encryptedContent) + } + ) + + val decryptResult = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult(), + onResult = { + if (it.resultCode != Activity.RESULT_OK) { + scope.launch { + Toast.makeText( + context, + "Sign request rejected", + Toast.LENGTH_SHORT + ).show() + } + return@rememberLauncherForActivityResult + } + + val decryptedContent = it.data?.getStringExtra("signature") ?: "" + val blockList = accountViewModel.account.getBlockList() + val privateTags = if (blockList == null) { + listOf(listOf("p", baseUser.pubkeyHex)) + } else { + if (accountViewModel.account.isHidden(baseUser)) { + blockList.privateTagsOrEmpty(decryptedContent).filter { element -> !element.contains(baseUser.pubkeyHex) } + } else { + blockList.privateTagsOrEmpty(decryptedContent).plus(element = listOf("p", baseUser.pubkeyHex)) + } + } + val msg = Event.mapper.writeValueAsString(privateTags) + + ServiceManager.shouldPauseService = true + AmberUtils.openAmber( + msg, + SignerType.NIP04_ENCRYPT, + encryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } + ) + WatchIsHiddenUser(baseUser, accountViewModel) { isHidden -> if (isHidden) { ShowUserButton { - accountViewModel.showUser(baseUser.pubkeyHex) + if (accountViewModel.loggedInWithAmber()) { + scope.launch(Dispatchers.IO) { + val blockList = accountViewModel.account.getBlockList() + val content = blockList?.content ?: "" + if (content.isBlank()) { + val privateTags = listOf(listOf("p", baseUser.pubkeyHex)) + val msg = Event.mapper.writeValueAsString(privateTags) + + AmberUtils.openAmber( + msg, + SignerType.NIP04_ENCRYPT, + encryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } else { + AmberUtils.openAmber( + content, + SignerType.NIP04_DECRYPT, + decryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } + } + } else { + accountViewModel.showUser(baseUser.pubkeyHex) + } } } else { DisplayFollowUnfollowButton(baseUser, accountViewModel) @@ -1737,9 +1841,9 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () -> val signedEvent = Event.fromJson(it) Client.send(signedEvent) LocalCache.verifyAndConsume(signedEvent, null) - event = null accountViewModel.account.live.invalidateData() accountViewModel.account.saveable.invalidateData() + event = null onDismiss() } }, @@ -1762,7 +1866,11 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () -> } val encryptedContent = it.data?.getStringExtra("signature") ?: "" - event = accountViewModel.hide(user, encryptedContent) + event = if (accountViewModel.account.isHidden(user)) { + accountViewModel.show(user, encryptedContent) + } else { + accountViewModel.hide(user, encryptedContent) + } } ) @@ -1785,7 +1893,11 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () -> val privateTags = if (blockList == null) { listOf(listOf("p", user.pubkeyHex)) } else { - blockList.privateTagsOrEmpty(decryptedContent).plus(element = listOf("p", user.pubkeyHex)) + if (accountViewModel.account.isHidden(user)) { + blockList.privateTagsOrEmpty(decryptedContent).filter { element -> !element.contains(user.pubkeyHex) } + } else { + blockList.privateTagsOrEmpty(decryptedContent).plus(element = listOf("p", user.pubkeyHex)) + } } val msg = Event.mapper.writeValueAsString(privateTags) @@ -1806,10 +1918,37 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () -> if (accountViewModel.userProfile() != user) { Divider() if (accountViewModel.account.isHidden(user)) { - DropdownMenuItem(onClick = { - accountViewModel.show(user) - onDismiss() - }) { + DropdownMenuItem( + onClick = { + if (accountViewModel.loggedInWithAmber()) { + scope.launch(Dispatchers.IO) { + val blockList = accountViewModel.account.getBlockList() + val content = blockList?.content ?: "" + if (content.isBlank()) { + val privateTags = listOf(listOf("p", user.pubkeyHex)) + val msg = Event.mapper.writeValueAsString(privateTags) + + AmberUtils.openAmber( + msg, + SignerType.NIP04_ENCRYPT, + encryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } else { + AmberUtils.openAmber( + content, + SignerType.NIP04_DECRYPT, + decryptResult, + accountViewModel.account.keyPair.pubKey.toHexKey() + ) + } + } + } else { + accountViewModel.show(user) + onDismiss() + } + } + ) { Text(stringResource(R.string.unblock_user)) } } else { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt index 4b047283e..80a05057b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt @@ -176,6 +176,24 @@ class PeopleListEvent( } } + fun removeUser(earlierVersion: PeopleListEvent, pubKeyHex: String, isPrivate: Boolean, encryptedContent: String, pubKey: HexKey, createdAt: Long = TimeUtils.now()): PeopleListEvent { + return if (isPrivate) { + create( + content = encryptedContent, + tags = earlierVersion.tags.filter { it.size > 1 && it[1] != pubKeyHex }, + pubKey = pubKey, + createdAt = createdAt + ) + } else { + create( + content = earlierVersion.content, + tags = earlierVersion.tags.filter { it.size > 1 && it[1] != pubKeyHex }, + pubKey = pubKey, + createdAt = createdAt + ) + } + } + fun removeUser(earlierVersion: PeopleListEvent, pubKeyHex: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent { if (!earlierVersion.isTaggedUser(pubKeyHex, isPrivate, privateKey)) return earlierVersion