mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-10-10 02:22:36 +02:00
Merge pull request #511 from greenart7c3/main
add option to send notes to selected relays
This commit is contained in:
@@ -628,19 +628,19 @@ class Account(
|
|||||||
return Pair(data, signedEvent)
|
return Pair(data, signedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendNip95(data: FileStorageEvent, signedEvent: FileStorageHeaderEvent): Note? {
|
fun sendNip95(data: FileStorageEvent, signedEvent: FileStorageHeaderEvent, relayList: List<Relay>? = null): Note? {
|
||||||
if (!isWriteable()) return null
|
if (!isWriteable()) return null
|
||||||
|
|
||||||
Client.send(data)
|
Client.send(data, relayList = relayList)
|
||||||
LocalCache.consume(data, null)
|
LocalCache.consume(data, null)
|
||||||
|
|
||||||
Client.send(signedEvent)
|
Client.send(signedEvent, relayList = relayList)
|
||||||
LocalCache.consume(signedEvent, null)
|
LocalCache.consume(signedEvent, null)
|
||||||
|
|
||||||
return LocalCache.notes[signedEvent.id]
|
return LocalCache.notes[signedEvent.id]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendHeader(headerInfo: FileHeader): Note? {
|
fun sendHeader(headerInfo: FileHeader, relayList: List<Relay>? = null): Note? {
|
||||||
if (!isWriteable()) return null
|
if (!isWriteable()) return null
|
||||||
|
|
||||||
val signedEvent = FileHeaderEvent.create(
|
val signedEvent = FileHeaderEvent.create(
|
||||||
@@ -655,7 +655,7 @@ class Account(
|
|||||||
privateKey = loggedIn.privKey!!
|
privateKey = loggedIn.privKey!!
|
||||||
)
|
)
|
||||||
|
|
||||||
Client.send(signedEvent)
|
Client.send(signedEvent, relayList = relayList)
|
||||||
LocalCache.consume(signedEvent, null)
|
LocalCache.consume(signedEvent, null)
|
||||||
|
|
||||||
return LocalCache.notes[signedEvent.id]
|
return LocalCache.notes[signedEvent.id]
|
||||||
@@ -671,7 +671,8 @@ class Account(
|
|||||||
zapRaiserAmount: Long? = null,
|
zapRaiserAmount: Long? = null,
|
||||||
replyingTo: String?,
|
replyingTo: String?,
|
||||||
root: String?,
|
root: String?,
|
||||||
directMentions: Set<HexKey>
|
directMentions: Set<HexKey>,
|
||||||
|
relayList: List<Relay>? = null
|
||||||
) {
|
) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
@@ -694,7 +695,7 @@ class Account(
|
|||||||
privateKey = loggedIn.privKey!!
|
privateKey = loggedIn.privKey!!
|
||||||
)
|
)
|
||||||
|
|
||||||
Client.send(signedEvent)
|
Client.send(signedEvent, relayList = relayList)
|
||||||
LocalCache.consume(signedEvent)
|
LocalCache.consume(signedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,7 +710,8 @@ class Account(
|
|||||||
closedAt: Int?,
|
closedAt: Int?,
|
||||||
zapReceiver: String? = null,
|
zapReceiver: String? = null,
|
||||||
wantsToMarkAsSensitive: Boolean,
|
wantsToMarkAsSensitive: Boolean,
|
||||||
zapRaiserAmount: Long? = null
|
zapRaiserAmount: Long? = null,
|
||||||
|
relayList: List<Relay>? = null
|
||||||
) {
|
) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
@@ -733,7 +735,7 @@ class Account(
|
|||||||
zapRaiserAmount = zapRaiserAmount
|
zapRaiserAmount = zapRaiserAmount
|
||||||
)
|
)
|
||||||
// println("Sending new PollNoteEvent: %s".format(signedEvent.toJson()))
|
// println("Sending new PollNoteEvent: %s".format(signedEvent.toJson()))
|
||||||
Client.send(signedEvent)
|
Client.send(signedEvent, relayList = relayList)
|
||||||
LocalCache.consume(signedEvent)
|
LocalCache.consume(signedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -73,10 +73,18 @@ object Client : RelayPool.Listener {
|
|||||||
RelayPool.sendFilterOnlyIfDisconnected()
|
RelayPool.sendFilterOnlyIfDisconnected()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun send(signedEvent: EventInterface, relay: String? = null, feedTypes: Set<FeedType>? = null, onDone: (() -> Unit)? = null) {
|
fun send(
|
||||||
|
signedEvent: EventInterface,
|
||||||
|
relay: String? = null,
|
||||||
|
feedTypes: Set<FeedType>? = null,
|
||||||
|
relayList: List<Relay>? = null,
|
||||||
|
onDone: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
if (relay == null) {
|
if (relayList != null) {
|
||||||
|
RelayPool.sendToSelectedRelays(relayList, signedEvent)
|
||||||
|
} else if (relay == null) {
|
||||||
RelayPool.send(signedEvent)
|
RelayPool.send(signedEvent)
|
||||||
} else {
|
} else {
|
||||||
val useConnectedRelayIfPresent = RelayPool.getRelays(relay)
|
val useConnectedRelayIfPresent = RelayPool.getRelays(relay)
|
||||||
|
@@ -62,6 +62,12 @@ object RelayPool : Relay.Listener {
|
|||||||
relays.forEach { it.sendFilterOnlyIfDisconnected() }
|
relays.forEach { it.sendFilterOnlyIfDisconnected() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendToSelectedRelays(list: List<Relay>, signedEvent: EventInterface) {
|
||||||
|
list.forEach { relay ->
|
||||||
|
relays.filter { it.url == relay.url }.forEach { it.send(signedEvent) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun send(signedEvent: EventInterface) {
|
fun send(signedEvent: EventInterface) {
|
||||||
relays.forEach { it.send(signedEvent) }
|
relays.forEach { it.send(signedEvent) }
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.vitorpamplona.amethyst.model.*
|
import com.vitorpamplona.amethyst.model.*
|
||||||
import com.vitorpamplona.amethyst.service.FileHeader
|
import com.vitorpamplona.amethyst.service.FileHeader
|
||||||
|
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||||
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
|
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@@ -56,7 +57,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upload(context: Context) {
|
fun upload(context: Context, relayList: List<Relay>? = null) {
|
||||||
isUploadingImage = true
|
isUploadingImage = true
|
||||||
|
|
||||||
val contentResolver = context.contentResolver
|
val contentResolver = context.contentResolver
|
||||||
@@ -78,7 +79,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
uploadingPercentage.value = 0.2f
|
uploadingPercentage.value = 0.2f
|
||||||
uploadingDescription.value = "Loading"
|
uploadingDescription.value = "Loading"
|
||||||
contentResolver.openInputStream(fileUri)?.use {
|
contentResolver.openInputStream(fileUri)?.use {
|
||||||
createNIP95Record(it.readBytes(), contentType, description, sensitiveContent)
|
createNIP95Record(it.readBytes(), contentType, description, sensitiveContent, relayList = relayList)
|
||||||
}
|
}
|
||||||
?: run {
|
?: run {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -98,7 +99,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
server = serverToUse,
|
server = serverToUse,
|
||||||
contentResolver = contentResolver,
|
contentResolver = contentResolver,
|
||||||
onSuccess = { imageUrl, mimeType ->
|
onSuccess = { imageUrl, mimeType ->
|
||||||
createNIP94Record(imageUrl, mimeType, description, sensitiveContent)
|
createNIP94Record(imageUrl, mimeType, description, sensitiveContent, relayList = relayList)
|
||||||
},
|
},
|
||||||
onError = {
|
onError = {
|
||||||
isUploadingImage = false
|
isUploadingImage = false
|
||||||
@@ -138,7 +139,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
return !isUploadingImage && galleryUri != null && selectedServer != null
|
return !isUploadingImage && galleryUri != null && selectedServer != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNIP94Record(imageUrl: String, mimeType: String?, description: String, sensitiveContent: Boolean) {
|
fun createNIP94Record(imageUrl: String, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||||
uploadingPercentage.value = 0.40f
|
uploadingPercentage.value = 0.40f
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
uploadingDescription.value = "Server Processing"
|
uploadingDescription.value = "Server Processing"
|
||||||
@@ -162,7 +163,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
onReady = {
|
onReady = {
|
||||||
uploadingPercentage.value = 0.90f
|
uploadingPercentage.value = 0.90f
|
||||||
uploadingDescription.value = "Sending"
|
uploadingDescription.value = "Sending"
|
||||||
account?.sendHeader(it)
|
account?.sendHeader(it, relayList)
|
||||||
uploadingPercentage.value = 1.00f
|
uploadingPercentage.value = 1.00f
|
||||||
isUploadingImage = false
|
isUploadingImage = false
|
||||||
onceUploaded()
|
onceUploaded()
|
||||||
@@ -191,7 +192,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNIP95Record(bytes: ByteArray, mimeType: String?, description: String, sensitiveContent: Boolean) {
|
fun createNIP95Record(bytes: ByteArray, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||||
uploadingPercentage.value = 0.30f
|
uploadingPercentage.value = 0.30f
|
||||||
uploadingDescription.value = "Hashing"
|
uploadingDescription.value = "Hashing"
|
||||||
|
|
||||||
@@ -210,7 +211,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
if (nip95 != null) {
|
if (nip95 != null) {
|
||||||
uploadingDescription.value = "Sending"
|
uploadingDescription.value = "Sending"
|
||||||
uploadingPercentage.value = 0.60f
|
uploadingPercentage.value = 0.60f
|
||||||
account?.sendNip95(nip95.first, nip95.second)
|
account?.sendNip95(nip95.first, nip95.second, relayList)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadingPercentage.value = 1.00f
|
uploadingPercentage.value = 1.00f
|
||||||
|
@@ -7,17 +7,21 @@ import android.util.Size
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.OutlinedTextField
|
import androidx.compose.material.OutlinedTextField
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
@@ -33,6 +37,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -70,6 +75,17 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var showRelaysDialog by remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
var relayList = account.activeRelays()?.filter {
|
||||||
|
it.write
|
||||||
|
}?.map {
|
||||||
|
it
|
||||||
|
} ?: account.convertLocalRelays().filter {
|
||||||
|
it.write
|
||||||
|
}
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = { onClose() },
|
onDismissRequest = { onClose() },
|
||||||
properties = DialogProperties(
|
properties = DialogProperties(
|
||||||
@@ -82,6 +98,20 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
|
if (showRelaysDialog) {
|
||||||
|
RelaySelectionDialog(
|
||||||
|
list = relayList,
|
||||||
|
onClose = {
|
||||||
|
showRelaysDialog = false
|
||||||
|
},
|
||||||
|
onPost = {
|
||||||
|
relayList = it
|
||||||
|
},
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp)
|
modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -97,10 +127,26 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac
|
|||||||
onClose()
|
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(
|
PostButton(
|
||||||
onPost = {
|
onPost = {
|
||||||
onClose()
|
onClose()
|
||||||
postViewModel.upload(context)
|
postViewModel.upload(context, relayList)
|
||||||
postViewModel.selectedServer?.let { account.changeDefaultFileServer(it) }
|
postViewModel.selectedServer?.let { account.changeDefaultFileServer(it) }
|
||||||
},
|
},
|
||||||
isActive = postViewModel.canPost()
|
isActive = postViewModel.canPost()
|
||||||
|
@@ -110,6 +110,16 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
|||||||
|
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
var showRelaysDialog by remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
var relayList = account.activeRelays()?.filter {
|
||||||
|
it.write
|
||||||
|
}?.map {
|
||||||
|
it
|
||||||
|
} ?: account.convertLocalRelays().filter {
|
||||||
|
it.write
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
postViewModel.load(account, baseReplyTo, quote)
|
postViewModel.load(account, baseReplyTo, quote)
|
||||||
@@ -144,6 +154,20 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
|
if (showRelaysDialog) {
|
||||||
|
RelaySelectionDialog(
|
||||||
|
list = relayList,
|
||||||
|
onClose = {
|
||||||
|
showRelaysDialog = false
|
||||||
|
},
|
||||||
|
onPost = {
|
||||||
|
relayList = it
|
||||||
|
},
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -165,10 +189,25 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
|||||||
onClose()
|
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(
|
PostButton(
|
||||||
onPost = {
|
onPost = {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
postViewModel.sendPost()
|
postViewModel.sendPost(relayList = relayList)
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -265,7 +304,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
|||||||
url,
|
url,
|
||||||
account.defaultFileServer,
|
account.defaultFileServer,
|
||||||
onAdd = { description, server, sensitiveContent ->
|
onAdd = { description, server, sensitiveContent ->
|
||||||
postViewModel.upload(url, description, sensitiveContent, server, context)
|
postViewModel.upload(url, description, sensitiveContent, server, context, relayList)
|
||||||
account.changeDefaultFileServer(server)
|
account.changeDefaultFileServer(server)
|
||||||
},
|
},
|
||||||
onCancel = {
|
onCancel = {
|
||||||
|
@@ -22,6 +22,7 @@ import com.vitorpamplona.amethyst.service.model.CommunityDefinitionEvent
|
|||||||
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
|
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
|
import com.vitorpamplona.amethyst.service.model.TextNoteEvent
|
||||||
import com.vitorpamplona.amethyst.service.noProtocolUrlValidator
|
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.MediaCompressor
|
||||||
import com.vitorpamplona.amethyst.ui.components.isValidURL
|
import com.vitorpamplona.amethyst.ui.components.isValidURL
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -128,7 +129,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
this.account = account
|
this.account = account
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendPost() {
|
fun sendPost(relayList: List<Relay>? = null) {
|
||||||
val tagger = NewMessageTagger(originalNote?.channelHex(), mentions, replyTos, message.text)
|
val tagger = NewMessageTagger(originalNote?.channelHex(), mentions, replyTos, message.text)
|
||||||
tagger.run()
|
tagger.run()
|
||||||
|
|
||||||
@@ -145,7 +146,20 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
val localZapRaiserAmount = if (wantsZapraiser) zapRaiserAmount else null
|
val localZapRaiserAmount = if (wantsZapraiser) zapRaiserAmount else null
|
||||||
|
|
||||||
if (wantsPoll) {
|
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) {
|
} else if (originalNote?.channelHex() != null) {
|
||||||
if (originalNote is AddressableEvent && originalNote?.address() != null) {
|
if (originalNote is AddressableEvent && originalNote?.address() != null) {
|
||||||
account?.sendLiveMessage(tagger.message, originalNote?.address()!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount)
|
account?.sendLiveMessage(tagger.message, originalNote?.address()!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive, localZapRaiserAmount)
|
||||||
@@ -172,14 +186,15 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
zapRaiserAmount = localZapRaiserAmount,
|
zapRaiserAmount = localZapRaiserAmount,
|
||||||
replyingTo = replyId,
|
replyingTo = replyId,
|
||||||
root = rootId,
|
root = rootId,
|
||||||
directMentions = tagger.directMentions
|
directMentions = tagger.directMentions,
|
||||||
|
relayList = relayList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upload(galleryUri: Uri, description: String, sensitiveContent: Boolean, server: ServersAvailable, context: Context) {
|
fun upload(galleryUri: Uri, description: String, sensitiveContent: Boolean, server: ServersAvailable, context: Context, relayList: List<Relay>? = null) {
|
||||||
isUploadingImage = true
|
isUploadingImage = true
|
||||||
contentToAddUrl = null
|
contentToAddUrl = null
|
||||||
|
|
||||||
@@ -194,7 +209,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
onReady = { fileUri, contentType, size ->
|
onReady = { fileUri, contentType, size ->
|
||||||
if (server == ServersAvailable.NIP95) {
|
if (server == ServersAvailable.NIP95) {
|
||||||
contentResolver.openInputStream(fileUri)?.use {
|
contentResolver.openInputStream(fileUri)?.use {
|
||||||
createNIP95Record(it.readBytes(), contentType, description, sensitiveContent)
|
createNIP95Record(it.readBytes(), contentType, description, sensitiveContent, relayList = relayList)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ImageUploader.uploadImage(
|
ImageUploader.uploadImage(
|
||||||
@@ -370,7 +385,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNIP94Record(imageUrl: String, mimeType: String?, description: String, sensitiveContent: Boolean) {
|
fun createNIP94Record(imageUrl: String, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
// Images don't seem to be ready immediately after upload
|
// Images don't seem to be ready immediately after upload
|
||||||
FileHeader.prepare(
|
FileHeader.prepare(
|
||||||
@@ -379,7 +394,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
description,
|
description,
|
||||||
sensitiveContent,
|
sensitiveContent,
|
||||||
onReady = {
|
onReady = {
|
||||||
val note = account?.sendHeader(it)
|
val note = account?.sendHeader(it, relayList = relayList)
|
||||||
|
|
||||||
isUploadingImage = false
|
isUploadingImage = false
|
||||||
|
|
||||||
@@ -401,7 +416,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNIP95Record(bytes: ByteArray, mimeType: String?, description: String, sensitiveContent: Boolean) {
|
fun createNIP95Record(bytes: ByteArray, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
FileHeader.prepare(
|
FileHeader.prepare(
|
||||||
bytes,
|
bytes,
|
||||||
@@ -411,7 +426,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
sensitiveContent,
|
sensitiveContent,
|
||||||
onReady = {
|
onReady = {
|
||||||
val nip95 = account?.createNip95(bytes, headerInfo = it)
|
val nip95 = account?.createNip95(bytes, headerInfo = it)
|
||||||
val note = nip95?.let { it1 -> account?.sendNip95(it1.first, it1.second) }
|
val note = nip95?.let { it1 -> account?.sendNip95(it1.first, it1.second, relayList = relayList) }
|
||||||
|
|
||||||
isUploadingImage = false
|
isUploadingImage = false
|
||||||
|
|
||||||
|
@@ -0,0 +1,183 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.Switch
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
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.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import com.vitorpamplona.amethyst.model.RelayInformation
|
||||||
|
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
data class RelayList(
|
||||||
|
val relay: Relay,
|
||||||
|
val isSelected: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun RelaySelectionDialog(
|
||||||
|
list: List<Relay>,
|
||||||
|
onClose: () -> Unit,
|
||||||
|
onPost: (list: List<Relay>) -> Unit,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val context = LocalContext.current
|
||||||
|
val relayList = accountViewModel.account.activeRelays()?.filter {
|
||||||
|
it.write
|
||||||
|
}?.map {
|
||||||
|
it
|
||||||
|
} ?: accountViewModel.account.convertLocalRelays().filter {
|
||||||
|
it.write
|
||||||
|
}
|
||||||
|
|
||||||
|
var relays by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
relayList.map {
|
||||||
|
RelayList(
|
||||||
|
it,
|
||||||
|
list.any { relay -> it.url == relay.url }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var relayInfo: RelayInformation? by remember { mutableStateOf(null) }
|
||||||
|
|
||||||
|
if (relayInfo != null) {
|
||||||
|
RelayInformationDialog(
|
||||||
|
onClose = {
|
||||||
|
relayInfo = null
|
||||||
|
},
|
||||||
|
relayInfo = relayInfo!!,
|
||||||
|
accountViewModel,
|
||||||
|
nav
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = { onClose() },
|
||||||
|
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 = {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
PostButton(
|
||||||
|
onPost = {
|
||||||
|
val selectedRelays = relays.filter { it.isSelected }
|
||||||
|
if (selectedRelays.isEmpty()) {
|
||||||
|
scope.launch {
|
||||||
|
Toast.makeText(context, "Select a relay to continue", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@PostButton
|
||||||
|
}
|
||||||
|
onPost(selectedRelays.map { it.relay })
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
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()
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = {
|
||||||
|
relays = relays.mapIndexed { j, item ->
|
||||||
|
if (index == j) {
|
||||||
|
item.copy(isSelected = !item.isSelected)
|
||||||
|
} else {
|
||||||
|
item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongClick = {
|
||||||
|
loadRelayInfo(item.relay.url, context, scope) {
|
||||||
|
relayInfo = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
item.relay.url
|
||||||
|
.removePrefix("ws://")
|
||||||
|
.removePrefix("wss://")
|
||||||
|
.removeSuffix("/")
|
||||||
|
)
|
||||||
|
Switch(
|
||||||
|
checked = item.isSelected,
|
||||||
|
onCheckedChange = {
|
||||||
|
relays = relays.mapIndexed { j, item ->
|
||||||
|
if (index == j) {
|
||||||
|
item.copy(isSelected = !item.isSelected)
|
||||||
|
} else { item }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user