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 b6e6793ae..00d9e598e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -503,7 +503,7 @@ class Account( returningContactList = if (keyPair.privKey == null) { ContactListEvent.followAddressableEvent(returningContactList, it, keyPair.pubKey.toHexKey()) } else { - ContactListEvent.followAddressableEvent(returningContactList, it, keyPair.privKey) + ContactListEvent.followAddressableEvent(returningContactList, it, keyPair.pubKey.toHexKey(), keyPair.privKey) } } } @@ -515,7 +515,7 @@ class Account( returningContactList = if (keyPair.privKey == null) { ContactListEvent.followEvent(returningContactList, it, keyPair.pubKey.toHexKey()) } else { - ContactListEvent.followEvent(returningContactList, it, keyPair.privKey) + ContactListEvent.followEvent(returningContactList, it, keyPair.pubKey.toHexKey(), keyPair.privKey) } } followingChannels = emptySet() @@ -557,13 +557,13 @@ class Account( return null } - fun follow(channel: Channel) { - if (!isWriteable()) return + fun follow(channel: Channel, signEvent: Boolean): ContactListEvent? { + if (!isWriteable() && signEvent) return null val contactList = migrateCommunitiesAndChannelsIfNeeded(userProfile().latestContactList) val event = if (contactList != null) { - ContactListEvent.followEvent(contactList, channel.idHex, keyPair.privKey!!) + ContactListEvent.followEvent(contactList, channel.idHex, keyPair.pubKey.toHexKey(), keyPair.privKey) } else { ContactListEvent.createFromScratch( followUsers = emptyList(), @@ -572,21 +572,27 @@ class Account( followCommunities = emptyList(), followEvents = DefaultChannels.toList().plus(channel.idHex), relayUse = Constants.defaultRelays.associate { it.url to ContactListEvent.ReadWrite(it.read, it.write) }, - privateKey = keyPair.privKey!! + publicKey = if (!signEvent) keyPair.pubKey else null, + privateKey = keyPair.privKey ) } + if (!signEvent) { + return event + } + Client.send(event) LocalCache.consume(event) + return null } - fun follow(community: AddressableNote) { - if (!isWriteable()) return + fun follow(community: AddressableNote, signEvent: Boolean): ContactListEvent? { + if (!isWriteable() && signEvent) return null val contactList = migrateCommunitiesAndChannelsIfNeeded(userProfile().latestContactList) val event = if (contactList != null) { - ContactListEvent.followAddressableEvent(contactList, community.address, keyPair.privKey!!) + ContactListEvent.followAddressableEvent(contactList, community.address, keyPair.pubKey.toHexKey(), keyPair.privKey) } else { val relays = Constants.defaultRelays.associate { it.url to ContactListEvent.ReadWrite(it.read, it.write) } ContactListEvent.createFromScratch( @@ -596,12 +602,18 @@ class Account( followCommunities = listOf(community.address), followEvents = DefaultChannels.toList(), relayUse = relays, - privateKey = keyPair.privKey!! + publicKey = if (!signEvent) keyPair.pubKey else null, + privateKey = keyPair.privKey ) } + if (!signEvent) { + return event + } + Client.send(event) LocalCache.consume(event) + return null } fun followHashtag(tag: String, signEvent: Boolean = true): ContactListEvent? { @@ -745,8 +757,8 @@ class Account( } } - fun unfollow(channel: Channel) { - if (!isWriteable()) return + fun unfollow(channel: Channel, signEvent: Boolean): ContactListEvent? { + if (!isWriteable() && signEvent) return null val contactList = migrateCommunitiesAndChannelsIfNeeded(userProfile().latestContactList) @@ -754,16 +766,22 @@ class Account( val event = ContactListEvent.unfollowEvent( contactList, channel.idHex, - keyPair.privKey!! + keyPair.pubKey.toHexKey(), + keyPair.privKey ) + if (!signEvent) { + return event + } + Client.send(event) LocalCache.consume(event) } + return null } - fun unfollow(community: AddressableNote) { - if (!isWriteable()) return + fun unfollow(community: AddressableNote, signEvent: Boolean): ContactListEvent? { + if (!isWriteable() && signEvent) return null val contactList = migrateCommunitiesAndChannelsIfNeeded(userProfile().latestContactList) @@ -771,12 +789,18 @@ class Account( val event = ContactListEvent.unfollowAddressableEvent( contactList, community.address, - keyPair.privKey!! + keyPair.pubKey.toHexKey(), + keyPair.privKey ) + if (!signEvent) { + return event + } + Client.send(event) LocalCache.consume(event) } + return null } fun createNip95(byteArray: ByteArray, headerInfo: FileHeader): Pair? { @@ -1131,7 +1155,7 @@ class Account( LocalCache.consume(event) LocalCache.getChannelIfExists(event.id)?.let { - follow(it) + follow(it, true) } } @@ -1597,7 +1621,7 @@ class Account( Client.send(event) LocalCache.consume(event) - follow(channel) + follow(channel, true) } fun unwrap(event: GiftWrapEvent): Event? { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt index 43c936dd0..ccee19640 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt @@ -253,9 +253,18 @@ class ContactListEvent( ) } - fun followEvent(earlierVersion: ContactListEvent, idHex: String, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ContactListEvent { + fun followEvent(earlierVersion: ContactListEvent, idHex: String, pubKey: HexKey, privateKey: ByteArray?, createdAt: Long = TimeUtils.now()): ContactListEvent { if (earlierVersion.isTaggedEvent(idHex)) return earlierVersion + if (privateKey == null) { + return create( + content = earlierVersion.content, + tags = earlierVersion.tags.plus(element = listOf("e", idHex)), + pubKey = pubKey, + createdAt = createdAt + ) + } + return create( content = earlierVersion.content, tags = earlierVersion.tags.plus(element = listOf("e", idHex)), @@ -275,9 +284,18 @@ class ContactListEvent( ) } - fun unfollowEvent(earlierVersion: ContactListEvent, idHex: String, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ContactListEvent { + fun unfollowEvent(earlierVersion: ContactListEvent, idHex: String, publicKey: HexKey, privateKey: ByteArray?, createdAt: Long = TimeUtils.now()): ContactListEvent { if (!earlierVersion.isTaggedEvent(idHex)) return earlierVersion + if (privateKey == null) { + return create( + content = earlierVersion.content, + tags = earlierVersion.tags.filter { it.size > 1 && it[1] != idHex }, + pubKey = publicKey, + createdAt = createdAt + ) + } + return create( content = earlierVersion.content, tags = earlierVersion.tags.filter { it.size > 1 && it[1] != idHex }, @@ -286,9 +304,18 @@ class ContactListEvent( ) } - fun followAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ContactListEvent { + fun followAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, pubKey: HexKey, privateKey: ByteArray?, createdAt: Long = TimeUtils.now()): ContactListEvent { if (earlierVersion.isTaggedAddressableNote(aTag.toTag())) return earlierVersion + if (privateKey == null) { + return create( + content = earlierVersion.content, + tags = earlierVersion.tags.plus(element = listOfNotNull("a", aTag.toTag(), aTag.relay)), + pubKey = pubKey, + createdAt = createdAt + ) + } + return create( content = earlierVersion.content, tags = earlierVersion.tags.plus(element = listOfNotNull("a", aTag.toTag(), aTag.relay)), @@ -308,9 +335,18 @@ class ContactListEvent( ) } - fun unfollowAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ContactListEvent { + fun unfollowAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, pubKey: HexKey, privateKey: ByteArray?, createdAt: Long = TimeUtils.now()): ContactListEvent { if (!earlierVersion.isTaggedAddressableNote(aTag.toTag())) return earlierVersion + if (privateKey == null) { + return create( + content = earlierVersion.content, + tags = earlierVersion.tags.filter { it.size > 1 && it[1] != aTag.toTag() }, + pubKey = pubKey, + createdAt = createdAt + ) + } + return create( content = earlierVersion.content, tags = earlierVersion.tags.filter { it.size > 1 && it[1] != aTag.toTag() }, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index 2c218a85b..56c4fe74c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -87,13 +87,17 @@ import com.vitorpamplona.amethyst.model.PublicChatChannel import com.vitorpamplona.amethyst.model.ServersAvailable import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.NostrChannelDataSource +import com.vitorpamplona.amethyst.service.PackageUtils +import com.vitorpamplona.amethyst.service.model.Event import com.vitorpamplona.amethyst.service.model.LiveActivitiesEvent.Companion.STATUS_LIVE import com.vitorpamplona.amethyst.service.model.Participant +import com.vitorpamplona.amethyst.service.relays.Client import com.vitorpamplona.amethyst.ui.actions.ImmutableListOfLists import com.vitorpamplona.amethyst.ui.actions.NewChannelView import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel import com.vitorpamplona.amethyst.ui.actions.PostButton +import com.vitorpamplona.amethyst.ui.actions.SignerDialog import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery import com.vitorpamplona.amethyst.ui.actions.toImmutableListOfLists import com.vitorpamplona.amethyst.ui.components.LoadNote @@ -1090,12 +1094,29 @@ private fun EditButton(accountViewModel: AccountViewModel, channel: PublicChatCh @Composable fun JoinChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (String) -> Unit) { 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) { + Client.send(it) + LocalCache.verifyAndConsume(it, null) + event = null + } + }, + event = event!! + ) + } Button( modifier = Modifier.padding(horizontal = 3.dp), onClick = { scope.launch(Dispatchers.IO) { - accountViewModel.account.follow(channel) + event = accountViewModel.account.follow(channel, !PackageUtils.isAmberInstalled(context)) } }, shape = ButtonBorder, @@ -1112,12 +1133,29 @@ fun JoinChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (S @Composable fun LeaveChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (String) -> Unit) { 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) { + Client.send(it) + LocalCache.verifyAndConsume(it, null) + event = null + } + }, + event = event!! + ) + } Button( modifier = Modifier.padding(horizontal = 3.dp), onClick = { scope.launch(Dispatchers.IO) { - accountViewModel.account.unfollow(channel) + event = accountViewModel.account.unfollow(channel, !PackageUtils.isAmberInstalled(context)) } }, shape = ButtonBorder, @@ -1134,12 +1172,29 @@ fun LeaveChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: ( @Composable fun JoinCommunityButton(accountViewModel: AccountViewModel, note: AddressableNote, nav: (String) -> Unit) { 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) { + Client.send(it) + LocalCache.verifyAndConsume(it, null) + event = null + } + }, + event = event!! + ) + } Button( modifier = Modifier.padding(horizontal = 3.dp), onClick = { scope.launch(Dispatchers.IO) { - accountViewModel.account.follow(note) + event = accountViewModel.account.follow(note, !PackageUtils.isAmberInstalled(context)) } }, shape = ButtonBorder, @@ -1156,12 +1211,29 @@ fun JoinCommunityButton(accountViewModel: AccountViewModel, note: AddressableNot @Composable fun LeaveCommunityButton(accountViewModel: AccountViewModel, note: AddressableNote, nav: (String) -> Unit) { 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) { + Client.send(it) + LocalCache.verifyAndConsume(it, null) + event = null + } + }, + event = event!! + ) + } Button( modifier = Modifier.padding(horizontal = 3.dp), onClick = { scope.launch(Dispatchers.IO) { - accountViewModel.account.unfollow(note) + event = accountViewModel.account.unfollow(note, !PackageUtils.isAmberInstalled(context)) } }, shape = ButtonBorder,