From 91488f035003f645d016512370359dd62b701e5a Mon Sep 17 00:00:00 2001 From: greenart7c3 <115044884+greenart7c3@users.noreply.github.com> Date: Mon, 17 Jul 2023 09:48:34 -0300 Subject: [PATCH] add option to send notes to selected relays --- .../vitorpamplona/amethyst/model/Account.kt | 10 +- .../amethyst/service/relays/Client.kt | 13 +- .../amethyst/service/relays/RelayPool.kt | 6 + .../amethyst/ui/actions/NewPostView.kt | 131 +++++++++++++++++- .../amethyst/ui/actions/NewPostViewModel.kt | 21 ++- 5 files changed, 171 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 c28e9fe10..47b27033d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -671,7 +671,8 @@ class Account( zapRaiserAmount: Long? = null, replyingTo: String?, root: String?, - directMentions: Set + directMentions: Set, + relayList: List? = null ) { if (!isWriteable()) return @@ -694,7 +695,7 @@ class Account( privateKey = loggedIn.privKey!! ) - Client.send(signedEvent) + Client.send(signedEvent, relayList = relayList) LocalCache.consume(signedEvent) } @@ -709,7 +710,8 @@ class Account( closedAt: Int?, zapReceiver: String? = null, wantsToMarkAsSensitive: Boolean, - zapRaiserAmount: Long? = null + zapRaiserAmount: Long? = null, + relayList: List? = null ) { if (!isWriteable()) return @@ -733,7 +735,7 @@ class Account( zapRaiserAmount = zapRaiserAmount ) // println("Sending new PollNoteEvent: %s".format(signedEvent.toJson())) - Client.send(signedEvent) + Client.send(signedEvent, relayList = relayList) LocalCache.consume(signedEvent) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Client.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Client.kt index f300f41f2..d20dc44b8 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Client.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Client.kt @@ -1,5 +1,6 @@ package com.vitorpamplona.amethyst.service.relays +import android.util.Log import com.vitorpamplona.amethyst.service.HttpClient import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.service.model.Event @@ -73,10 +74,18 @@ object Client : RelayPool.Listener { RelayPool.sendFilterOnlyIfDisconnected() } - fun send(signedEvent: EventInterface, relay: String? = null, feedTypes: Set? = null, onDone: (() -> Unit)? = null) { + fun send( + signedEvent: EventInterface, + relay: String? = null, + feedTypes: Set? = null, + relayList: List? = null, + onDone: (() -> Unit)? = null + ) { checkNotInMainThread() - if (relay == null) { + if (relayList != null) { + RelayPool.sendToSelectedRelays(relayList, signedEvent) + } else if (relay == null) { RelayPool.send(signedEvent) } else { val useConnectedRelayIfPresent = RelayPool.getRelays(relay) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt index 0d2140c18..e42293631 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt @@ -62,6 +62,12 @@ object RelayPool : Relay.Listener { relays.forEach { it.sendFilterOnlyIfDisconnected() } } + fun sendToSelectedRelays(list: List, signedEvent: EventInterface) { + list.forEach { relay -> + relays.filter { it.url == relay.url }.forEach { it.send(signedEvent) } + } + } + fun send(signedEvent: EventInterface) { relays.forEach { it.send(signedEvent) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index 4fec46b38..f2a9407d8 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -9,6 +9,7 @@ import android.widget.Toast import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed @@ -71,6 +72,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource import com.vitorpamplona.amethyst.service.noProtocolUrlValidator +import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.ui.components.* import com.vitorpamplona.amethyst.ui.note.CancelIcon import com.vitorpamplona.amethyst.ui.note.CloseIcon @@ -95,6 +97,11 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +data class RelayList( + val relay: Relay, + val isSelected: Boolean +) + @OptIn(ExperimentalComposeUiApi::class) @Composable fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, accountViewModel: AccountViewModel, nav: (String) -> Unit) { @@ -110,6 +117,27 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n val scrollState = rememberScrollState() val scope = rememberCoroutineScope() + var showRelaysDialog by remember { + mutableStateOf(false) + } + val relayList = account.activeRelays()?.filter { + it.write + }?.map { + it + } ?: account.convertLocalRelays().filter { + it.write + } + + var relays by remember { + mutableStateOf( + relayList.map { + RelayList( + it, + true + ) + } + ) + } LaunchedEffect(Unit) { postViewModel.load(account, baseReplyTo, quote) @@ -144,6 +172,90 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n .fillMaxWidth() .fillMaxHeight() ) { + if (showRelaysDialog) { + Dialog( + onDismissRequest = { showRelaysDialog = false }, + properties = DialogProperties( + usePlatformDefaultWidth = false, + dismissOnClickOutside = false, + decorFitsSystemWindows = false + ) + ) { + Surface( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .padding(start = 10.dp, end = 10.dp, top = 10.dp) + + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + CloseButton( + onCancel = { + showRelaysDialog = false + } + ) + + PostButton( + onPost = { + scope.launch(Dispatchers.IO) { + showRelaysDialog = false + } + }, + isActive = true + ) + } + + LazyColumn( + contentPadding = PaddingValues( + top = 10.dp, + bottom = 10.dp + ) + ) { + itemsIndexed( + relays, + key = { _, item -> item.relay.url } + ) { index, item -> + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clickable { + relays = relays.mapIndexed { j, item -> + if (index == j) { + item.copy(isSelected = !item.isSelected) + } else { item } + } + } + ) { + Text(text = item.relay.url) + Switch( + checked = item.isSelected, + onCheckedChange = { + relays = relays.mapIndexed { j, item -> + if (index == j) { + item.copy(isSelected = !item.isSelected) + } else { item } + } + } + ) + } + } + } + } + } + } + } + Column( modifier = Modifier .fillMaxWidth() @@ -165,10 +277,27 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n onClose() }) + Box { + IconButton( + modifier = Modifier.align(Alignment.Center), + onClick = { + showRelaysDialog = true + } + ) { + Icon( + painter = painterResource(R.drawable.relays), + contentDescription = null, + modifier = Modifier.height(25.dp), + tint = MaterialTheme.colors.onBackground + ) + } + } PostButton( onPost = { + val list = relays.filter { it.isSelected }.map { it.relay } + scope.launch(Dispatchers.IO) { - postViewModel.sendPost() + postViewModel.sendPost(relayList = list) onClose() } }, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index ec1cbb04a..dcaa20007 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -22,6 +22,7 @@ import com.vitorpamplona.amethyst.service.model.CommunityDefinitionEvent import com.vitorpamplona.amethyst.service.model.PrivateDmEvent import com.vitorpamplona.amethyst.service.model.TextNoteEvent import com.vitorpamplona.amethyst.service.noProtocolUrlValidator +import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.ui.components.MediaCompressor import com.vitorpamplona.amethyst.ui.components.isValidURL import kotlinx.coroutines.Dispatchers @@ -128,7 +129,7 @@ open class NewPostViewModel() : ViewModel() { this.account = account } - fun sendPost() { + fun sendPost(relayList: List? = null) { val tagger = NewMessageTagger(originalNote?.channelHex(), mentions, replyTos, message.text) tagger.run() @@ -145,7 +146,20 @@ open class NewPostViewModel() : ViewModel() { val localZapRaiserAmount = if (wantsZapraiser) zapRaiserAmount else null if (wantsPoll) { - account?.sendPoll(tagger.message, tagger.replyTos, tagger.mentions, pollOptions, valueMaximum, valueMinimum, consensusThreshold, closedAt, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount) + account?.sendPoll( + tagger.message, + tagger.replyTos, + tagger.mentions, + pollOptions, + valueMaximum, + valueMinimum, + consensusThreshold, + closedAt, + zapReceiver, + wantsToMarkAsSensitive, + localZapRaiserAmount, + relayList + ) } else if (originalNote?.channelHex() != null) { if (originalNote is AddressableEvent && originalNote?.address() != null) { account?.sendLiveMessage(tagger.message, originalNote?.address()!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount) @@ -172,7 +186,8 @@ open class NewPostViewModel() : ViewModel() { zapRaiserAmount = localZapRaiserAmount, replyingTo = replyId, root = rootId, - directMentions = tagger.directMentions + directMentions = tagger.directMentions, + relayList = relayList ) }